From fd5cef6b429a8676e5e2523cf7c942331939b5e0 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 17 Sep 2021 20:20:36 +0200 Subject: [PATCH] Support DataDirectory placeholder in LocalDB connection string --- .../Configuration/ConfigConnectionString.cs | 48 +++++++++++++++++-- .../Migrations/Install/DatabaseBuilder.cs | 4 -- .../Runtime/CoreRuntime.cs | 2 - .../UmbracoBuilderExtensions.cs | 3 ++ 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Configuration/ConfigConnectionString.cs b/src/Umbraco.Core/Configuration/ConfigConnectionString.cs index d0dec2548c..18bdace632 100644 --- a/src/Umbraco.Core/Configuration/ConfigConnectionString.cs +++ b/src/Umbraco.Core/Configuration/ConfigConnectionString.cs @@ -14,8 +14,43 @@ namespace Umbraco.Cms.Core.Configuration public ConfigConnectionString(string name, string connectionString, string providerName = null) { Name = name ?? throw new ArgumentNullException(nameof(name)); - ConnectionString = connectionString; - ProviderName = string.IsNullOrEmpty(providerName) ? ParseProviderName(connectionString) : providerName; + ConnectionString = ParseConnectionString(connectionString, ref providerName); + ProviderName = providerName; + } + + private static string ParseConnectionString(string connectionString, ref string providerName) + { + if (string.IsNullOrEmpty(connectionString)) + { + return null; + } + + var builder = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + // Replace data directory placeholder + const string attachDbFileNameKey = "AttachDbFileName"; + const string dataDirectoryPlaceholder = "|DataDirectory|"; + if (builder.TryGetValue(attachDbFileNameKey, out var attachDbFileNameValue) && + attachDbFileNameValue is string attachDbFileName && + attachDbFileName.Contains(dataDirectoryPlaceholder)) + { + var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + if (!string.IsNullOrEmpty(dataDirectory)) + { + builder[attachDbFileNameKey] = attachDbFileName.Replace(dataDirectoryPlaceholder, dataDirectory); + } + } + + // Also parse provider name now we already have a builder + if (string.IsNullOrEmpty(providerName)) + { + providerName = ParseProviderName(builder); + } + + return builder.ToString(); } /// @@ -37,13 +72,18 @@ namespace Umbraco.Cms.Core.Configuration ConnectionString = connectionString }; + return ParseProviderName(builder); + } + + private static string ParseProviderName(DbConnectionStringBuilder builder) + { if ((builder.TryGetValue("Data Source", out var dataSource) || builder.TryGetValue("DataSource", out dataSource)) && dataSource?.ToString().EndsWith(".sdf", StringComparison.OrdinalIgnoreCase) == true) { - return Cms.Core.Constants.DbProviderNames.SqlCe; + return Constants.DbProviderNames.SqlCe; } - return Cms.Core.Constants.DbProviderNames.SqlServer; + return Constants.DbProviderNames.SqlServer; } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs index a4efa29e00..0a1def06ef 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs @@ -160,10 +160,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install string connectionString = LocalDbConnectionString; const string providerName = Constants.DbProviderNames.SqlServer; - // Replace data directory placeholder (this is not supported by LocalDB) - var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); - connectionString = connectionString.Replace("|DataDirectory|", dataDirectory); - _configManipulator.SaveConnectionString(connectionString, providerName); _databaseFactory.Configure(connectionString, providerName); diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index f9a8578dc3..86f4e070c2 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -94,8 +94,6 @@ namespace Umbraco.Cms.Infrastructure.Runtime _logger.LogError(exception, msg); }; - AppDomain.CurrentDomain.SetData("DataDirectory", _hostingEnvironment?.MapPathContentRoot(Constants.SystemDirectories.Data)); - // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate AcquireMainDom(); diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 4319bc3544..90307cf746 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -94,6 +94,9 @@ namespace Umbraco.Extensions services.AddLogger(tempHostingEnvironment, loggingConfig, config); + // The DataDirectory is used to resolve database file paths (directly supported by SQL CE and manually replaced for LocalDB) + AppDomain.CurrentDomain.SetData("DataDirectory", tempHostingEnvironment?.MapPathContentRoot(Constants.SystemDirectories.Data)); + // Manually create and register the HttpContextAccessor. In theory this should not be registered // again by the user but if that is the case it's not the end of the world since HttpContextAccessor // is just based on AsyncLocal, see https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http/src/HttpContextAccessor.cs