diff --git a/src/Umbraco.Core/Configuration/ConfigureConnectionStrings.cs b/src/Umbraco.Core/Configuration/ConfigureConnectionStrings.cs
index bd982c18b9..829d19bb53 100644
--- a/src/Umbraco.Core/Configuration/ConfigureConnectionStrings.cs
+++ b/src/Umbraco.Core/Configuration/ConfigureConnectionStrings.cs
@@ -6,16 +6,18 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Configuration;
///
-/// Configures ConnectionStrings.
+/// Configures the named option.
///
public class ConfigureConnectionStrings : IConfigureNamedOptions
{
private readonly IConfiguration _configuration;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public ConfigureConnectionStrings(IConfiguration configuration) => _configuration = configuration;
+ /// The configuration.
+ public ConfigureConnectionStrings(IConfiguration configuration)
+ => _configuration = configuration;
///
public void Configure(ConnectionStrings options) => Configure(Options.DefaultName, options);
@@ -35,7 +37,7 @@ public class ConfigureConnectionStrings : IConfigureNamedOptions
+/// Represents a single connection string.
+///
+public class ConnectionStrings // TODO: Rename to [Umbraco]ConnectionString (since v10 this only contains a single connection string)
{
private string? _connectionString;
@@ -15,20 +17,42 @@ public class ConnectionStrings
///
/// The DataDirectory placeholder.
///
- public const string DataDirectoryPlaceholder = "|DataDirectory|";
+ public const string DataDirectoryPlaceholder = ConfigurationExtensions.DataDirectoryPlaceholder;
///
/// The postfix used to identify a connection strings provider setting.
///
- public const string ProviderNamePostfix = "_ProviderName";
+ public const string ProviderNamePostfix = ConfigurationExtensions.ProviderNamePostfix;
+ ///
+ /// Gets or sets the name.
+ ///
+ ///
+ /// The name.
+ ///
+ [Obsolete("This property will be removed in Umbraco 12, because this class is now using named options.")]
public string? Name { get; set; }
+ ///
+ /// Gets or sets the connection string.
+ ///
+ ///
+ /// The connection string.
+ ///
+ ///
+ /// When set, the will be replaced with the actual physical path.
+ ///
public string? ConnectionString
{
get => _connectionString;
- set => _connectionString = value?.ReplaceDataDirectoryPlaceholder();
+ set => _connectionString = ConfigurationExtensions.ReplaceDataDirectoryPlaceholder(value);
}
- public string? ProviderName { get; set; } = DefaultProviderName;
+ ///
+ /// Gets or sets the name of the provider.
+ ///
+ ///
+ /// The name of the provider.
+ ///
+ public string? ProviderName { get; set; }
}
diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs
index 8b19781d39..0ad9852671 100644
--- a/src/Umbraco.Core/Constants-System.cs
+++ b/src/Umbraco.Core/Constants-System.cs
@@ -1,4 +1,4 @@
- namespace Umbraco.Cms.Core
+namespace Umbraco.Cms.Core
{
public static partial class Constants
{
@@ -62,7 +62,9 @@
public const string UmbracoDefaultDatabaseName = "Umbraco";
- public const string UmbracoConnectionName = "umbracoDbDSN";public const string DefaultUmbracoPath = "~/umbraco";
+ public const string UmbracoConnectionName = "umbracoDbDSN";
+
+ public const string DefaultUmbracoPath = "~/umbraco";
}
}
}
diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
index a8a374fef2..57607d347a 100644
--- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
+++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
@@ -1,5 +1,3 @@
-using System;
-using System.Collections.Generic;
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -9,105 +7,101 @@ using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Configuration.Models.Validation;
using Umbraco.Extensions;
-namespace Umbraco.Cms.Core.DependencyInjection
+namespace Umbraco.Cms.Core.DependencyInjection;
+
+///
+/// Extension methods for
+///
+public static partial class UmbracoBuilderExtensions
{
- ///
- /// Extension methods for
- ///
- public static partial class UmbracoBuilderExtensions
+ private static IUmbracoBuilder AddUmbracoOptions(this IUmbracoBuilder builder, Action>? configure = null)
+ where TOptions : class
{
-
- private static IUmbracoBuilder AddUmbracoOptions(this IUmbracoBuilder builder, Action>? configure = null)
- where TOptions : class
+ var umbracoOptionsAttribute = typeof(TOptions).GetCustomAttribute();
+ if (umbracoOptionsAttribute is null)
{
- var umbracoOptionsAttribute = typeof(TOptions).GetCustomAttribute();
- if (umbracoOptionsAttribute is null)
+ throw new ArgumentException($"{typeof(TOptions)} do not have the UmbracoOptionsAttribute.");
+ }
+
+ var optionsBuilder = builder.Services.AddOptions()
+ .Bind(
+ builder.Config.GetSection(umbracoOptionsAttribute.ConfigurationKey),
+ o => o.BindNonPublicProperties = umbracoOptionsAttribute.BindNonPublicProperties)
+ .ValidateDataAnnotations();
+
+ configure?.Invoke(optionsBuilder);
+
+ return builder;
+ }
+
+ ///
+ /// Add Umbraco configuration services and options
+ ///
+ public static IUmbracoBuilder AddConfiguration(this IUmbracoBuilder builder)
+ {
+ // Register configuration validators.
+ builder.Services.AddSingleton, ContentSettingsValidator>();
+ builder.Services.AddSingleton, GlobalSettingsValidator>();
+ builder.Services.AddSingleton, HealthChecksSettingsValidator>();
+ builder.Services.AddSingleton, RequestHandlerSettingsValidator>();
+ builder.Services.AddSingleton, UnattendedSettingsValidator>();
+
+ // Register configuration sections.
+ builder
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions(optionsBuilder => optionsBuilder.PostConfigure(options =>
{
- throw new ArgumentException($"{typeof(TOptions)} do not have the UmbracoOptionsAttribute.");
- }
-
- var optionsBuilder = builder.Services.AddOptions()
- .Bind(
- builder.Config.GetSection(umbracoOptionsAttribute.ConfigurationKey),
- o => o.BindNonPublicProperties = umbracoOptionsAttribute.BindNonPublicProperties
- )
- .ValidateDataAnnotations();
-
- configure?.Invoke(optionsBuilder);
-
- return builder;
- }
-
- ///
- /// Add Umbraco configuration services and options
- ///
- public static IUmbracoBuilder AddConfiguration(this IUmbracoBuilder builder)
- {
- // Register configuration validators.
- builder.Services.AddSingleton, ContentSettingsValidator>();
- builder.Services.AddSingleton, GlobalSettingsValidator>();
- builder.Services.AddSingleton, HealthChecksSettingsValidator>();
- builder.Services.AddSingleton, RequestHandlerSettingsValidator>();
- builder.Services.AddSingleton, UnattendedSettingsValidator>();
-
- // Register configuration sections.
- builder
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions(optionsBuilder => optionsBuilder.PostConfigure(options =>
+ if (string.IsNullOrEmpty(options.UmbracoMediaPhysicalRootPath))
{
- if (string.IsNullOrEmpty(options.UmbracoMediaPhysicalRootPath))
- {
- options.UmbracoMediaPhysicalRootPath = options.UmbracoMediaPath;
- }
- }))
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions()
- .AddUmbracoOptions();
+ options.UmbracoMediaPhysicalRootPath = options.UmbracoMediaPath;
+ }
+ }))
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions()
+ .AddUmbracoOptions();
- builder.Services.AddSingleton, ConfigureConnectionStrings>();
+ builder.Services.AddSingleton, ConfigureConnectionStrings>();
- builder.Services.Configure(
- Constants.Configuration.NamedOptions.InstallDefaultData.Languages,
- builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.Languages}"));
- builder.Services.Configure(
- Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes,
- builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes}"));
- builder.Services.Configure(
- Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes,
- builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes}"));
- builder.Services.Configure(
- Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes,
- builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes}"));
+ builder.Services.Configure(
+ Constants.Configuration.NamedOptions.InstallDefaultData.Languages,
+ builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.Languages}"));
+ builder.Services.Configure(
+ Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes,
+ builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.DataTypes}"));
+ builder.Services.Configure(
+ Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes,
+ builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.MediaTypes}"));
+ builder.Services.Configure(
+ Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes,
+ builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes}"));
- builder.Services.Configure(options => options.MergeReplacements(builder.Config));
+ builder.Services.Configure(options => options.MergeReplacements(builder.Config));
- return builder;
- }
+ return builder;
}
}
diff --git a/src/Umbraco.Core/Extensions/ConfigurationExtensions.cs b/src/Umbraco.Core/Extensions/ConfigurationExtensions.cs
new file mode 100644
index 0000000000..d719876d9f
--- /dev/null
+++ b/src/Umbraco.Core/Extensions/ConfigurationExtensions.cs
@@ -0,0 +1,103 @@
+using Microsoft.Extensions.Configuration;
+using Umbraco.Cms.Core;
+
+namespace Umbraco.Extensions;
+
+///
+/// Extension methods for configuration.
+///
+public static class ConfigurationExtensions
+{
+ ///
+ /// The DataDirectory name.
+ ///
+ internal const string DataDirectoryName = "DataDirectory";
+
+ ///
+ /// The DataDirectory placeholder.
+ ///
+ internal const string DataDirectoryPlaceholder = "|DataDirectory|";
+
+ ///
+ /// The postfix used to identify a connection string provider setting.
+ ///
+ internal const string ProviderNamePostfix = "_ProviderName";
+
+ ///
+ /// Gets the provider name for the connection string name (shorthand for GetSection("ConnectionStrings")[name + "_ProviderName"]).
+ ///
+ /// The configuration.
+ /// The connection string key.
+ ///
+ /// The provider name.
+ ///
+ ///
+ /// This uses the same convention as the Configuration API for connection string environment variables.
+ ///
+ public static string? GetConnectionStringProviderName(this IConfiguration configuration, string name)
+ => configuration.GetConnectionString(name + ProviderNamePostfix);
+
+ ///
+ /// Gets the Umbraco connection string (shorthand for GetSection("ConnectionStrings")[name] and replacing the |DataDirectory| placeholder).
+ ///
+ /// The configuration.
+ /// The connection string key.
+ ///
+ /// The Umbraco connection string.
+ ///
+ public static string? GetUmbracoConnectionString(this IConfiguration configuration, string name = Constants.System.UmbracoConnectionName)
+ => configuration.GetUmbracoConnectionString(name, out _);
+
+ ///
+ /// Gets the Umbraco connection string and provider name (shorthand for GetSection("ConnectionStrings")[Constants.System.UmbracoConnectionName] and replacing the |DataDirectory| placeholder).
+ ///
+ /// The configuration.
+ /// The provider name.
+ ///
+ /// The Umbraco connection string.
+ ///
+ public static string? GetUmbracoConnectionString(this IConfiguration configuration, out string? providerName)
+ => configuration.GetUmbracoConnectionString(Constants.System.UmbracoConnectionName, out providerName);
+
+ ///
+ /// Gets the Umbraco connection string and provider name (shorthand for GetSection("ConnectionStrings")[name] and replacing the |DataDirectory| placeholder).
+ ///
+ /// The configuration.
+ /// The name.
+ /// The provider name.
+ ///
+ /// The Umbraco connection string.
+ ///
+ public static string? GetUmbracoConnectionString(this IConfiguration configuration, string name, out string? providerName)
+ {
+ string? connectionString = configuration.GetConnectionString(name);
+ if (!string.IsNullOrEmpty(connectionString))
+ {
+ // Replace data directory
+ connectionString = ReplaceDataDirectoryPlaceholder(connectionString);
+
+ // Get provider name
+ providerName = configuration.GetConnectionStringProviderName(name);
+ }
+ else
+ {
+ providerName = null;
+ }
+
+ return connectionString;
+ }
+
+ internal static string? ReplaceDataDirectoryPlaceholder(string? connectionString)
+ {
+ if (!string.IsNullOrEmpty(connectionString))
+ {
+ string? dataDirectory = AppDomain.CurrentDomain.GetData(DataDirectoryName)?.ToString();
+ if (!string.IsNullOrEmpty(dataDirectory))
+ {
+ return connectionString.Replace(DataDirectoryPlaceholder, dataDirectory);
+ }
+ }
+
+ return connectionString;
+ }
+}
diff --git a/src/Umbraco.Core/Extensions/ConnectionStringExtensions.cs b/src/Umbraco.Core/Extensions/ConnectionStringExtensions.cs
index 6af569e514..3077a570dc 100644
--- a/src/Umbraco.Core/Extensions/ConnectionStringExtensions.cs
+++ b/src/Umbraco.Core/Extensions/ConnectionStringExtensions.cs
@@ -1,35 +1,24 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
-using System;
-using Microsoft.Extensions.Configuration;
-using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
-namespace Umbraco.Extensions
+namespace Umbraco.Extensions;
+
+///
+/// Extension methods for a connection string.
+///
+public static class ConnectionStringExtensions
{
- public static class ConnectionStringExtensions
- {
- public static bool IsConnectionStringConfigured(this ConnectionStrings connectionString)
- => connectionString != null &&
- !string.IsNullOrWhiteSpace(connectionString.ConnectionString) &&
- !string.IsNullOrWhiteSpace(connectionString.ProviderName);
-
- ///
- /// Gets a connection string from configuration with placeholders replaced.
- ///
- public static string? GetUmbracoConnectionString(
- this IConfiguration configuration,
- string connectionStringName = Constants.System.UmbracoConnectionName) =>
- configuration.GetConnectionString(connectionStringName).ReplaceDataDirectoryPlaceholder();
-
- ///
- /// Replaces instances of the |DataDirectory| placeholder in a string with the value of AppDomain DataDirectory.
- ///
- public static string? ReplaceDataDirectoryPlaceholder(this string input)
- {
- var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
- return input?.Replace(ConnectionStrings.DataDirectoryPlaceholder, dataDirectory);
- }
- }
+ ///
+ /// Determines whether the connection string is configured (set to a non-empty value).
+ ///
+ /// The connection string.
+ ///
+ /// true if the connection string is configured; otherwise, false.
+ ///
+ public static bool IsConnectionStringConfigured(this ConnectionStrings connectionString)
+ => connectionString != null &&
+ !string.IsNullOrWhiteSpace(connectionString.ConnectionString) &&
+ !string.IsNullOrWhiteSpace(connectionString.ProviderName);
}
diff --git a/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseConfigureStep.cs b/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseConfigureStep.cs
index 44879f31b8..f01b65aaa7 100644
--- a/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseConfigureStep.cs
+++ b/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseConfigureStep.cs
@@ -1,7 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
@@ -13,9 +9,7 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
{
- [InstallSetupStep(InstallationType.NewInstall,
- "DatabaseConfigure", "database", 10, "Setting up a database, so Umbraco has a place to store your website",
- PerformsAppRestart = true)]
+ [InstallSetupStep(InstallationType.NewInstall, "DatabaseConfigure", "database", 10, "Setting up a database, so Umbraco has a place to store your website", PerformsAppRestart = true)]
public class DatabaseConfigureStep : InstallSetupStep
{
private readonly DatabaseBuilder _databaseBuilder;
@@ -45,44 +39,33 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
return Task.FromResult(null);
}
- public override object ViewModel
+ public override object ViewModel => new
{
- get
- {
- var options = _databaseProviderMetadata
- .Where(x => x.IsAvailable)
- .OrderBy(x => x.SortOrder)
- .ToList();
+ databases = _databaseProviderMetadata.GetAvailable().ToList()
+ };
- return new
- {
- databases = options
- };
- }
- }
-
- public override string View => ShouldDisplayView() ? base.View : "";
+ public override string View => ShouldDisplayView() ? base.View : string.Empty;
public override bool RequiresExecution(DatabaseModel model) => ShouldDisplayView();
private bool ShouldDisplayView()
{
- //If the connection string is already present in web.config we don't need to show the settings page and we jump to installing/upgrading.
+ // If the connection string is already present in web.config we don't need to show the settings page and we jump to installing/upgrading.
var databaseSettings = _connectionStrings.Get(Core.Constants.System.UmbracoConnectionName);
-
if (databaseSettings.IsConnectionStringConfigured())
{
try
{
- //Since a connection string was present we verify the db can connect and query
- _ = _databaseBuilder.ValidateSchema();
+ // Since a connection string was present we verify the db can connect and query
+ _databaseBuilder.ValidateSchema();
return false;
}
catch (Exception ex)
{
+ // Something went wrong, could not connect so probably need to reconfigure
_logger.LogError(ex, "An error occurred, reconfiguring...");
- //something went wrong, could not connect so probably need to reconfigure
+
return true;
}
}
diff --git a/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs
index 38ac000452..4f5dbc3cc2 100644
--- a/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs
+++ b/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs
@@ -1,10 +1,5 @@
-using System;
-using System.Collections.Generic;
using System.Collections.Specialized;
-using System.Linq;
-using System.Net.Http;
using System.Text;
-using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Umbraco.Cms.Core.Configuration.Models;
@@ -118,10 +113,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
{
get
{
- var quickInstallSettings = _databaseProviderMetadata
- .Where(x => x.SupportsQuickInstall)
- .Where(x => x.IsAvailable)
- .OrderBy(x => x.SortOrder)
+ var quickInstallSettings = _databaseProviderMetadata.GetAvailable(true)
.Select(x => new
{
displayName = x.DisplayName,
diff --git a/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs b/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs
index 312106ecca..3b891b88c4 100644
--- a/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs
+++ b/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs
@@ -69,10 +69,10 @@ namespace Umbraco.Cms.Infrastructure.Install
break;
case RuntimeLevelReason.UpgradePackageMigrations:
{
- if (!_runtimeState.StartupState.TryGetValue(RuntimeState.PendingPacakgeMigrationsStateKey, out var pm)
+ if (!_runtimeState.StartupState.TryGetValue(RuntimeState.PendingPackageMigrationsStateKey, out var pm)
|| pm is not IReadOnlyList pendingMigrations)
{
- throw new InvalidOperationException($"The required key {RuntimeState.PendingPacakgeMigrationsStateKey} does not exist in startup state");
+ throw new InvalidOperationException($"The required key {RuntimeState.PendingPackageMigrationsStateKey} does not exist in startup state");
}
if (pendingMigrations.Count == 0)
diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
index 542494458e..c4a37d4374 100644
--- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
using System.Data.Common;
-using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
@@ -139,20 +136,15 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
// if the database model is null then we will attempt quick install.
if (databaseSettings == null)
{
- providerMeta = _databaseProviderMetadata
- .OrderBy(x => x.SortOrder)
- .Where(x => x.SupportsQuickInstall)
- .FirstOrDefault(x => x.IsAvailable);
-
+ providerMeta = _databaseProviderMetadata.GetAvailable(true).FirstOrDefault();
databaseSettings = new DatabaseModel
{
- DatabaseName = providerMeta?.DefaultDatabaseName!,
+ DatabaseName = providerMeta?.DefaultDatabaseName!
};
}
else
{
- providerMeta = _databaseProviderMetadata
- .FirstOrDefault(x => x.Id == databaseSettings.DatabaseProviderMetadataId);
+ providerMeta = _databaseProviderMetadata.FirstOrDefault(x => x.Id == databaseSettings.DatabaseProviderMetadataId);
}
if (providerMeta == null)
@@ -177,7 +169,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
return true;
}
-
private void Configure(string connectionString, string? providerName, bool installMissingDatabase)
{
// Update existing connection string
diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseProviderMetadataExtensions.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseProviderMetadataExtensions.cs
new file mode 100644
index 0000000000..d0ad59fbb8
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Persistence/DatabaseProviderMetadataExtensions.cs
@@ -0,0 +1,55 @@
+using Umbraco.Cms.Core.Install.Models;
+
+namespace Umbraco.Cms.Infrastructure.Persistence;
+
+///
+/// Extension methods for .
+///
+public static class DatabaseProviderMetadataExtensions
+{
+ ///
+ /// Gets the available database provider metadata.
+ ///
+ /// The database provider metadata.
+ /// If set to true only returns providers that support quick install.
+ ///
+ /// The available database provider metadata.
+ ///
+ public static IEnumerable GetAvailable(this IEnumerable databaseProviderMetadata, bool onlyQuickInstall = false)
+ => databaseProviderMetadata.Where(x => (!onlyQuickInstall || x.SupportsQuickInstall) && x.IsAvailable).OrderBy(x => x.SortOrder);
+
+ ///
+ /// Determines whether a database can be created for the specified provider name while ignoring the value of .
+ ///
+ /// The database provider metadata.
+ /// The name of the provider.
+ ///
+ /// true if a database can be created for the specified provider name; otherwise, false.
+ ///
+ public static bool CanForceCreateDatabase(this IEnumerable databaseProviderMetadata, string? providerName)
+ => databaseProviderMetadata.FirstOrDefault(x => x.ProviderName == providerName)?.ForceCreateDatabase == true;
+
+ ///
+ /// Generates the connection string.
+ ///
+ /// The database provider metadata.
+ /// The name of the database, uses the default database name when null.
+ /// The server.
+ /// The login.
+ /// The password.
+ /// Indicates whether integrated authentication should be used (when supported by the provider).
+ ///
+ /// The generated connection string.
+ ///
+ public static string? GenerateConnectionString(this IDatabaseProviderMetadata databaseProviderMetadata, string? databaseName = null, string? server = null, string? login = null, string? password = null, bool? integratedAuth = null)
+ => databaseProviderMetadata.GenerateConnectionString(new DatabaseModel()
+ {
+ DatabaseProviderMetadataId = databaseProviderMetadata.Id,
+ ProviderName = databaseProviderMetadata.ProviderName,
+ DatabaseName = databaseName ?? databaseProviderMetadata.DefaultDatabaseName,
+ Server = server ?? string.Empty,
+ Login = login ?? string.Empty,
+ Password = password ?? string.Empty,
+ IntegratedAuth = integratedAuth == true && databaseProviderMetadata.SupportsIntegratedAuthentication
+ });
+}
diff --git a/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs b/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs
index 5ab9993f43..ca41e200f2 100644
--- a/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs
+++ b/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs
@@ -1,6 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -14,7 +11,6 @@ using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Web.Common.DependencyInjection;
-using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Runtime
{
@@ -24,7 +20,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime
///
public class RuntimeState : IRuntimeState
{
- internal const string PendingPacakgeMigrationsStateKey = "PendingPackageMigrations";
+ internal const string PendingPackageMigrationsStateKey = "PendingPackageMigrations";
+
private readonly IOptions _globalSettings = null!;
private readonly IOptions _unattendedSettings = null!;
private readonly IUmbracoVersion _umbracoVersion = null!;
@@ -33,6 +30,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
private readonly PendingPackageMigrations _packageMigrationState = null!;
private readonly Dictionary _startupState = new Dictionary();
private readonly IConflictingRouteService _conflictingRouteService = null!;
+ private readonly IEnumerable _databaseProviderMetadata = null!;
///
/// The initial
@@ -41,17 +39,17 @@ namespace Umbraco.Cms.Infrastructure.Runtime
public static RuntimeState Booting() => new RuntimeState() { Level = RuntimeLevel.Boot };
private RuntimeState()
- {
- }
+ { }
public RuntimeState(
- IOptions globalSettings,
- IOptions unattendedSettings,
- IUmbracoVersion umbracoVersion,
- IUmbracoDatabaseFactory databaseFactory,
- ILogger logger,
- PendingPackageMigrations packageMigrationState,
- IConflictingRouteService conflictingRouteService)
+ IOptions globalSettings,
+ IOptions unattendedSettings,
+ IUmbracoVersion umbracoVersion,
+ IUmbracoDatabaseFactory databaseFactory,
+ ILogger logger,
+ PendingPackageMigrations packageMigrationState,
+ IConflictingRouteService conflictingRouteService,
+ IEnumerable databaseProviderMetadata)
{
_globalSettings = globalSettings;
_unattendedSettings = unattendedSettings;
@@ -60,8 +58,29 @@ namespace Umbraco.Cms.Infrastructure.Runtime
_logger = logger;
_packageMigrationState = packageMigrationState;
_conflictingRouteService = conflictingRouteService;
+ _databaseProviderMetadata = databaseProviderMetadata;
}
+ [Obsolete("Use ctor with all params. This will be removed in Umbraco 12")]
+ public RuntimeState(
+ IOptions globalSettings,
+ IOptions unattendedSettings,
+ IUmbracoVersion umbracoVersion,
+ IUmbracoDatabaseFactory databaseFactory,
+ ILogger logger,
+ PendingPackageMigrations packageMigrationState,
+ IConflictingRouteService conflictingRouteService)
+ : this(
+ globalSettings,
+ unattendedSettings,
+ umbracoVersion,
+ databaseFactory,
+ logger,
+ packageMigrationState,
+ StaticServiceProvider.Instance.GetRequiredService(),
+ StaticServiceProvider.Instance.GetServices())
+ { }
+
///
/// Initializes a new instance of the class.
///
@@ -81,8 +100,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
logger,
packageMigrationState,
StaticServiceProvider.Instance.GetRequiredService())
- {
- }
+ { }
///
public Version Version => _umbracoVersion.Version;
@@ -143,7 +161,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
// cannot connect to configured database, this is bad, fail
_logger.LogDebug("Could not connect to database.");
- if (_globalSettings.Value.InstallMissingDatabase || CanAutoInstallMissingDatabase(_databaseFactory))
+ if (_globalSettings.Value.InstallMissingDatabase || _databaseProviderMetadata.CanForceCreateDatabase(_databaseFactory.ProviderName))
{
// ok to install on a configured but missing database
Level = RuntimeLevel.Install;
@@ -257,7 +275,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
IReadOnlyList packagesRequiringMigration = _packageMigrationState.GetPendingPackageMigrations(keyValues);
if (packagesRequiringMigration.Count > 0)
{
- _startupState[PendingPacakgeMigrationsStateKey] = packagesRequiringMigration;
+ _startupState[PendingPackageMigrationsStateKey] = packagesRequiringMigration;
return UmbracoDatabaseState.NeedsPackageMigration;
}
@@ -311,8 +329,5 @@ namespace Umbraco.Cms.Infrastructure.Runtime
return canConnect;
}
-
- private bool CanAutoInstallMissingDatabase(IUmbracoDatabaseFactory databaseFactory)
- => databaseFactory.ConnectionString?.InvariantContains("(localdb)") == true;
}
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Configuration/Models/ConnectionStringsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Configuration/Models/ConnectionStringsTests.cs
deleted file mode 100644
index 5398dfc7fd..0000000000
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Configuration/Models/ConnectionStringsTests.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using System;
-using NUnit.Framework;
-using Umbraco.Cms.Core.Configuration;
-using Umbraco.Cms.Core.Configuration.Models;
-using Umbraco.Cms.Tests.UnitTests.AutoFixture;
-using Constants = Umbraco.Cms.Core.Constants;
-
-namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Configuration.Models
-{
- [TestFixture]
- public class ConnectionStringsTests
- {
- [Test]
- public void ProviderName_WhenNotExplicitlySet_HasDefaultSet()
- {
- var sut = new ConnectionStrings();
- Assert.That(sut.ProviderName, Is.EqualTo(ConnectionStrings.DefaultProviderName));
- }
-
- [Test]
- [AutoMoqData]
- public void ConnectionString_WhenSetterCalled_ReplacesDataDirectoryPlaceholder(string aDataDirectory)
- {
- AppDomain.CurrentDomain.SetData("DataDirectory", aDataDirectory);
-
- var sut = new ConnectionStrings
- {
- ConnectionString = $"{ConnectionStrings.DataDirectoryPlaceholder}/foo"
- };
- Assert.That(sut.ConnectionString, Contains.Substring($"{aDataDirectory}/foo"));
- }
- }
-}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Configuration/ConfigureConnectionStringsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Configuration/ConfigureConnectionStringsTests.cs
similarity index 95%
rename from tests/Umbraco.Tests.UnitTests/Umbraco.Configuration/ConfigureConnectionStringsTests.cs
rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Configuration/ConfigureConnectionStringsTests.cs
index 4e7d6f7f1f..98ec3e01e6 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Configuration/ConfigureConnectionStringsTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Configuration/ConfigureConnectionStringsTests.cs
@@ -9,7 +9,7 @@ using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Tests.UnitTests.AutoFixture;
-namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Configuration;
+namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Configuration;
[TestFixture]
public class ConfigureConnectionStringsTests
@@ -27,7 +27,7 @@ public class ConfigureConnectionStringsTests
var configuration = configurationBuilder.Build();
var services = new ServiceCollection();
- services.AddOptions().Bind(configuration.GetSection("ConnectionStrings"));
+ services.AddOptions();
services.AddSingleton, ConfigureConnectionStrings>();
services.AddSingleton(configuration);
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ConfigurationExtensionsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ConfigurationExtensionsTests.cs
new file mode 100644
index 0000000000..94fbc6fcfa
--- /dev/null
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ConfigurationExtensionsTests.cs
@@ -0,0 +1,130 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+using System;
+using Microsoft.Extensions.Configuration;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Cms.Core;
+using Umbraco.Cms.Core.Configuration.Models;
+using Umbraco.Extensions;
+
+namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Extensions
+{
+ [TestFixture]
+ public class ConfigurationExtensionsTests
+ {
+ private const string DataDirectory = @"C:\Data";
+
+ [Test]
+ public void CanParseSqlServerConnectionString()
+ {
+ const string ConfiguredConnectionString = @"Server=.\SQLEXPRESS;Database=UmbracoCms;Integrated Security=true";
+
+ Mock mockedConfig = CreateConfig(ConfiguredConnectionString);
+
+ string connectionString = mockedConfig.Object.GetUmbracoConnectionString(out string providerName);
+
+ AssertResults(
+ ConfiguredConnectionString,
+ "Microsoft.Data.SqlClient",
+ connectionString,
+ providerName);
+ }
+
+ [Test]
+ public void CanParseLocalDbConnectionString()
+ {
+ const string ConfiguredConnectionString = @"Server=(LocalDb)\MyInstance;Integrated Security=true;";
+
+ Mock mockedConfig = CreateConfig(ConfiguredConnectionString);
+
+ string connectionString = mockedConfig.Object.GetUmbracoConnectionString(out string providerName);
+
+ AssertResults(
+ ConfiguredConnectionString,
+ "Microsoft.Data.SqlClient",
+ connectionString,
+ providerName);
+ }
+
+ [Test]
+ public void CanParseLocalDbConnectionStringWithDataDirectory()
+ {
+ const string ConfiguredConnectionString = @"Data Source=(LocalDb)\MyInstance;Initial Catalog=UmbracoDb;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\Umbraco.mdf";
+
+ Mock mockedConfig = CreateConfig(ConfiguredConnectionString);
+ SetDataDirectory();
+
+ string connectionString = mockedConfig.Object.GetUmbracoConnectionString(out string providerName);
+
+ AssertResults(
+ @"Data Source=(LocalDb)\MyInstance;Initial Catalog=UmbracoDb;Integrated Security=SSPI;AttachDBFilename=C:\Data\Umbraco.mdf",
+ "Microsoft.Data.SqlClient",
+ connectionString,
+ providerName);
+ }
+
+ [Test]
+ public void CanParseSQLiteConnectionStringWithDataDirectory()
+ {
+ const string ConfiguredConnectionString = "Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True";
+ const string ConfiguredProviderName = "Microsoft.Data.SQLite";
+
+ Mock mockedConfig = CreateConfig(ConfiguredConnectionString, ConfiguredProviderName);
+ SetDataDirectory();
+
+ string connectionString = mockedConfig.Object.GetUmbracoConnectionString(out string providerName);
+
+ AssertResults(
+ @"Data Source=C:\Data/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True",
+ "Microsoft.Data.SQLite",
+ connectionString,
+ providerName);
+ }
+
+ [Test]
+ public void CanParseConnectionStringWithNamedProvider()
+ {
+ const string ConfiguredConnectionString = @"Server=.\SQLEXPRESS;Database=UmbracoCms;Integrated Security=true";
+ const string ConfiguredProviderName = "MyProvider";
+
+ Mock mockedConfig = CreateConfig(ConfiguredConnectionString, ConfiguredProviderName);
+
+ string connectionString = mockedConfig.Object.GetUmbracoConnectionString(out string providerName);
+
+ AssertResults(
+ ConfiguredConnectionString,
+ ConfiguredProviderName,
+ connectionString,
+ providerName);
+ }
+
+ private static Mock CreateConfig(string configuredConnectionString, string configuredProviderName = ConnectionStrings.DefaultProviderName)
+ {
+ var mockConfigSection = new Mock();
+ mockConfigSection
+ .SetupGet(m => m[It.Is(s => s == Constants.System.UmbracoConnectionName)])
+ .Returns(configuredConnectionString);
+ mockConfigSection
+ .SetupGet(m => m[It.Is(s => s == $"{Constants.System.UmbracoConnectionName}{ConnectionStrings.ProviderNamePostfix}")])
+ .Returns(configuredProviderName);
+
+ var mockedConfig = new Mock();
+ mockedConfig
+ .Setup(a => a.GetSection(It.Is(s => s == "ConnectionStrings")))
+ .Returns(mockConfigSection.Object);
+
+ return mockedConfig;
+ }
+
+ private static void SetDataDirectory() =>
+ AppDomain.CurrentDomain.SetData("DataDirectory", DataDirectory);
+
+ private static void AssertResults(string expectedConnectionString, string expectedProviderName, string connectionString, string providerName)
+ {
+ Assert.AreEqual(expectedConnectionString, connectionString);
+ Assert.AreEqual(expectedProviderName, providerName);
+ }
+ }
+}