Merge pull request #11127 from umbraco/v9/feature/localdb-install-option
Add LocalDB database install option and implement automatic database creation
This commit is contained in:
@@ -5,69 +5,88 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
{
|
||||
public class ConfigConnectionString
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public string ConnectionString { get; }
|
||||
|
||||
public string ProviderName { get; }
|
||||
|
||||
public ConfigConnectionString(string name, string connectionString, string providerName = null)
|
||||
{
|
||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
ConnectionString = connectionString;
|
||||
|
||||
ProviderName = string.IsNullOrEmpty(providerName) ? ParseProvider(connectionString) : providerName;
|
||||
ConnectionString = ParseConnectionString(connectionString, ref providerName);
|
||||
ProviderName = providerName;
|
||||
}
|
||||
|
||||
public string ConnectionString { get; }
|
||||
public string ProviderName { get; }
|
||||
public string Name { get; }
|
||||
|
||||
private static bool IsSqlCe(DbConnectionStringBuilder builder) => (builder.TryGetValue("Data Source", out var ds)
|
||||
|| builder.TryGetValue("DataSource", out ds)) &&
|
||||
ds is string dataSource &&
|
||||
dataSource.EndsWith(".sdf");
|
||||
|
||||
private static bool IsSqlServer(DbConnectionStringBuilder builder) =>
|
||||
!string.IsNullOrEmpty(GetServer(builder)) &&
|
||||
((builder.TryGetValue("Database", out var db) && db is string database &&
|
||||
!string.IsNullOrEmpty(database)) ||
|
||||
(builder.TryGetValue("AttachDbFileName", out var a) && a is string attachDbFileName &&
|
||||
!string.IsNullOrEmpty(attachDbFileName)) ||
|
||||
(builder.TryGetValue("Initial Catalog", out var i) && i is string initialCatalog &&
|
||||
!string.IsNullOrEmpty(initialCatalog)));
|
||||
|
||||
private static string GetServer(DbConnectionStringBuilder builder)
|
||||
private static string ParseConnectionString(string connectionString, ref string providerName)
|
||||
{
|
||||
if(builder.TryGetValue("Server", out var s) && s is string server)
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
return server;
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
if ((builder.TryGetValue("Data Source", out var ds)
|
||||
|| builder.TryGetValue("DataSource", out ds)) && ds is string dataSource)
|
||||
var builder = new DbConnectionStringBuilder
|
||||
{
|
||||
return dataSource;
|
||||
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);
|
||||
|
||||
// Mutate the existing connection string (note: the builder also lowercases the properties)
|
||||
connectionString = builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
// Also parse provider name now we already have a builder
|
||||
if (string.IsNullOrEmpty(providerName))
|
||||
{
|
||||
providerName = ParseProviderName(builder);
|
||||
}
|
||||
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
private static string ParseProvider(string connectionString)
|
||||
/// <summary>
|
||||
/// Parses the connection string to get the provider name.
|
||||
/// </summary>
|
||||
/// <param name="connectionString">The connection string.</param>
|
||||
/// <returns>
|
||||
/// The provider name or <c>null</c> is the connection string is empty.
|
||||
/// </returns>
|
||||
public static string ParseProviderName(string connectionString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var builder = new DbConnectionStringBuilder {ConnectionString = connectionString};
|
||||
if (IsSqlCe(builder))
|
||||
var builder = new DbConnectionStringBuilder
|
||||
{
|
||||
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 Constants.DbProviderNames.SqlCe;
|
||||
}
|
||||
|
||||
|
||||
if (IsSqlServer(builder))
|
||||
{
|
||||
return Constants.DbProviderNames.SqlServer;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Cannot determine provider name from connection string",
|
||||
nameof(connectionString));
|
||||
return Constants.DbProviderNames.SqlServer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,6 @@ namespace Umbraco.Cms.Core.Configuration.Models
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the Umbraco database connection string..
|
||||
/// </summary>
|
||||
public ConfigConnectionString UmbracoConnectionString { get; set; }
|
||||
public ConfigConnectionString UmbracoConnectionString { get; set; } = new ConfigConnectionString(Constants.System.UmbracoConnectionName, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
@@ -12,35 +8,8 @@ namespace Umbraco.Extensions
|
||||
public static class ConfigConnectionStringExtensions
|
||||
{
|
||||
public static bool IsConnectionStringConfigured(this ConfigConnectionString databaseSettings)
|
||||
{
|
||||
var dbIsSqlCe = false;
|
||||
if (databaseSettings?.ProviderName != null)
|
||||
{
|
||||
dbIsSqlCe = databaseSettings.ProviderName == Constants.DbProviderNames.SqlCe;
|
||||
}
|
||||
|
||||
var sqlCeDatabaseExists = false;
|
||||
if (dbIsSqlCe)
|
||||
{
|
||||
var parts = databaseSettings.ConnectionString.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var dataSourcePart = parts.FirstOrDefault(x => x.InvariantStartsWith("Data Source="));
|
||||
if (dataSourcePart != null)
|
||||
{
|
||||
var datasource = dataSourcePart.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString());
|
||||
var filePath = datasource.Replace("Data Source=", string.Empty);
|
||||
sqlCeDatabaseExists = File.Exists(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Either the connection details are not fully specified or it's a SQL CE database that doesn't exist yet
|
||||
if (databaseSettings == null
|
||||
|| string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) || string.IsNullOrWhiteSpace(databaseSettings.ProviderName)
|
||||
|| (dbIsSqlCe && sqlCeDatabaseExists == false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
=> databaseSettings != null &&
|
||||
!string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) &&
|
||||
!string.IsNullOrWhiteSpace(databaseSettings.ProviderName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Install.Models
|
||||
{
|
||||
[DataContract(Name = "database", Namespace = "")]
|
||||
public class DatabaseModel
|
||||
{
|
||||
public DatabaseModel()
|
||||
{
|
||||
//defaults
|
||||
DatabaseType = DatabaseType.SqlCe;
|
||||
}
|
||||
|
||||
[DataMember(Name = "dbType")]
|
||||
public DatabaseType DatabaseType { get; set; }
|
||||
public DatabaseType DatabaseType { get; set; } = DatabaseType.SqlServer;
|
||||
|
||||
[DataMember(Name = "server")]
|
||||
public string Server { get; set; }
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace Umbraco.Cms.Core.Install.Models
|
||||
namespace Umbraco.Cms.Core.Install.Models
|
||||
{
|
||||
public enum DatabaseType
|
||||
{
|
||||
SqlLocalDb,
|
||||
SqlCe,
|
||||
SqlServer,
|
||||
SqlAzure,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -10,7 +7,6 @@ using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Install.Models;
|
||||
using Umbraco.Cms.Core.Net;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
@@ -50,14 +46,11 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
_userAgentProvider = userAgentProvider;
|
||||
_umbracoDatabaseFactory = umbracoDatabaseFactory;
|
||||
|
||||
//We need to initialize the type already, as we can't detect later, if the connection string is added on the fly.
|
||||
// We need to initialize the type already, as we can't detect later, if the connection string is added on the fly.
|
||||
GetInstallationType();
|
||||
}
|
||||
|
||||
public InstallationType GetInstallationType()
|
||||
{
|
||||
return _installationType ?? (_installationType = IsBrandNewInstall ? InstallationType.NewInstall : InstallationType.Upgrade).Value;
|
||||
}
|
||||
public InstallationType GetInstallationType() => _installationType ??= IsBrandNewInstall ? InstallationType.NewInstall : InstallationType.Upgrade;
|
||||
|
||||
public async Task SetInstallStatusAsync(bool isCompleted, string errorMsg)
|
||||
{
|
||||
@@ -65,25 +58,14 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
{
|
||||
var userAgent = _userAgentProvider.GetUserAgent();
|
||||
|
||||
// Check for current install Id
|
||||
var installId = Guid.NewGuid();
|
||||
|
||||
// Check for current install ID
|
||||
var installCookie = _cookieManager.GetCookieValue(Constants.Web.InstallerCookieName);
|
||||
if (string.IsNullOrEmpty(installCookie) == false)
|
||||
if (!Guid.TryParse(installCookie, out var installId))
|
||||
{
|
||||
if (Guid.TryParse(installCookie, out installId))
|
||||
{
|
||||
// check that it's a valid Guid
|
||||
if (installId == Guid.Empty)
|
||||
installId = Guid.NewGuid();
|
||||
}
|
||||
else
|
||||
{
|
||||
installId = Guid.NewGuid(); // Guid.TryParse will have reset installId to Guid.Empty
|
||||
}
|
||||
}
|
||||
installId = Guid.NewGuid();
|
||||
|
||||
_cookieManager.SetCookieValue(Constants.Web.InstallerCookieName, installId.ToString());
|
||||
_cookieManager.SetCookieValue(Constants.Web.InstallerCookieName, installId.ToString());
|
||||
}
|
||||
|
||||
var dbProvider = string.Empty;
|
||||
if (IsBrandNewInstall == false)
|
||||
@@ -108,29 +90,14 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this is a brand new install meaning that there is no configured version and there is no configured database connection
|
||||
/// Checks if this is a brand new install, meaning that there is no configured database connection or the database is empty.
|
||||
/// </summary>
|
||||
private bool IsBrandNewInstall
|
||||
{
|
||||
get
|
||||
{
|
||||
var databaseSettings = _connectionStrings.CurrentValue.UmbracoConnectionString;
|
||||
if (databaseSettings.IsConnectionStringConfigured() == false)
|
||||
{
|
||||
//no version or conn string configured, must be a brand new install
|
||||
return true;
|
||||
}
|
||||
|
||||
//now we have to check if this is really a new install, the db might be configured and might contain data
|
||||
|
||||
if (databaseSettings.IsConnectionStringConfigured() == false
|
||||
|| _databaseBuilder.IsDatabaseConfigured == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _databaseBuilder.IsUmbracoInstalled() == false;
|
||||
}
|
||||
}
|
||||
/// <value>
|
||||
/// <c>true</c> if this is a brand new install; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
private bool IsBrandNewInstall => _connectionStrings.CurrentValue.UmbracoConnectionString?.IsConnectionStringConfigured() != true ||
|
||||
_databaseBuilder.IsDatabaseConfigured == false ||
|
||||
_databaseBuilder.CanConnectToDatabase == false ||
|
||||
_databaseBuilder.IsUmbracoInstalled() == false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -7,6 +8,7 @@ using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Install;
|
||||
using Umbraco.Cms.Core.Install.Models;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
@@ -33,13 +35,24 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
if (database == null)
|
||||
{
|
||||
database = new DatabaseModel();
|
||||
|
||||
if (IsLocalDbAvailable())
|
||||
{
|
||||
database.DatabaseType = DatabaseType.SqlLocalDb;
|
||||
}
|
||||
else if (IsSqlCeAvailable())
|
||||
{
|
||||
database.DatabaseType = DatabaseType.SqlCe;
|
||||
}
|
||||
}
|
||||
|
||||
if (_databaseBuilder.CanConnect(database.DatabaseType.ToString(), database.ConnectionString, database.Server, database.DatabaseName, database.Login, database.Password, database.IntegratedAuth) == false)
|
||||
{
|
||||
throw new InstallException("Could not connect to the database");
|
||||
}
|
||||
|
||||
ConfigureConnection(database);
|
||||
|
||||
return Task.FromResult<InstallSetupResult>(null);
|
||||
}
|
||||
|
||||
@@ -49,6 +62,10 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
_databaseBuilder.ConfigureDatabaseConnection(database.ConnectionString);
|
||||
}
|
||||
else if (database.DatabaseType == DatabaseType.SqlLocalDb)
|
||||
{
|
||||
_databaseBuilder.ConfigureSqlLocalDbDatabaseConnection();
|
||||
}
|
||||
else if (database.DatabaseType == DatabaseType.SqlCe)
|
||||
{
|
||||
_databaseBuilder.ConfigureEmbeddedDatabaseConnection();
|
||||
@@ -62,9 +79,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
var password = database.Password.Replace("'", "''");
|
||||
password = string.Format("'{0}'", password);
|
||||
|
||||
_databaseBuilder.ConfigureDatabaseConnection(
|
||||
database.Server, database.DatabaseName, database.Login, password,
|
||||
database.DatabaseType.ToString());
|
||||
_databaseBuilder.ConfigureDatabaseConnection(database.Server, database.DatabaseName, database.Login, password, database.DatabaseType.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,28 +99,32 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
databases.Insert(0, new { name = "Microsoft SQL Server Compact (SQL CE)", id = DatabaseType.SqlCe.ToString() });
|
||||
}
|
||||
|
||||
if (IsLocalDbAvailable())
|
||||
{
|
||||
// Ensure this is always inserted as first when available
|
||||
databases.Insert(0, new { name = "Microsoft SQL Server Express (LocalDB)", id = DatabaseType.SqlLocalDb.ToString() });
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
databases = databases
|
||||
databases
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsSqlCeAvailable()
|
||||
{
|
||||
public static bool IsLocalDbAvailable() => new LocalDb().IsAvailable;
|
||||
|
||||
public static bool IsSqlCeAvailable() =>
|
||||
// NOTE: Type.GetType will only return types that are currently loaded into the appdomain. In this case
|
||||
// that is ok because we know if this is availalbe we will have manually loaded it into the appdomain.
|
||||
// Else we'd have to use Assembly.LoadFrom and need to know the DLL location here which we don't need to do.
|
||||
return !(Type.GetType("Umbraco.Cms.Persistence.SqlCe.SqlCeSyntaxProvider, Umbraco.Persistence.SqlCe") is null);
|
||||
}
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
||||
!(Type.GetType("Umbraco.Cms.Persistence.SqlCe.SqlCeSyntaxProvider, Umbraco.Persistence.SqlCe") is null);
|
||||
|
||||
public override string View => ShouldDisplayView() ? base.View : "";
|
||||
|
||||
|
||||
public override bool RequiresExecution(DatabaseModel model)
|
||||
{
|
||||
return ShouldDisplayView();
|
||||
}
|
||||
public override bool RequiresExecution(DatabaseModel model) => ShouldDisplayView();
|
||||
|
||||
private bool ShouldDisplayView()
|
||||
{
|
||||
@@ -118,6 +137,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
//Since a connection string was present we verify the db can connect and query
|
||||
_ = _databaseBuilder.ValidateSchema();
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -9,17 +9,16 @@ using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
[InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade,
|
||||
"DatabaseInstall", 11, "")]
|
||||
[InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade, "DatabaseInstall", 11, "")]
|
||||
public class DatabaseInstallStep : InstallSetupStep<object>
|
||||
{
|
||||
private readonly DatabaseBuilder _databaseBuilder;
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly DatabaseBuilder _databaseBuilder;
|
||||
|
||||
public DatabaseInstallStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime)
|
||||
public DatabaseInstallStep(IRuntimeState runtime, DatabaseBuilder databaseBuilder)
|
||||
{
|
||||
_databaseBuilder = databaseBuilder;
|
||||
_runtime = runtime;
|
||||
_databaseBuilder = databaseBuilder;
|
||||
}
|
||||
|
||||
public override Task<InstallSetupResult> ExecuteAsync(object model)
|
||||
@@ -27,6 +26,11 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
if (_runtime.Level == RuntimeLevel.Run)
|
||||
throw new Exception("Umbraco is already configured!");
|
||||
|
||||
if (_runtime.Reason == RuntimeLevelReason.InstallMissingDatabase)
|
||||
{
|
||||
_databaseBuilder.CreateDatabase();
|
||||
}
|
||||
|
||||
var result = _databaseBuilder.CreateSchemaAndData();
|
||||
|
||||
if (result.Success == false)
|
||||
@@ -39,16 +43,13 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
return Task.FromResult<InstallSetupResult>(null);
|
||||
}
|
||||
|
||||
//upgrade is required so set the flag for the next step
|
||||
// Upgrade is required, so set the flag for the next step
|
||||
return Task.FromResult(new InstallSetupResult(new Dictionary<string, object>
|
||||
{
|
||||
{"upgrade", true}
|
||||
{ "upgrade", true}
|
||||
}));
|
||||
}
|
||||
|
||||
public override bool RequiresExecution(object model)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public override bool RequiresExecution(object model) => true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Exceptions;
|
||||
@@ -20,7 +21,7 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
private readonly DatabaseSchemaCreatorFactory _databaseSchemaCreatorFactory;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
private readonly IOptions<GlobalSettings> _globalSettings;
|
||||
private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator;
|
||||
private readonly ILogger<UnattendedInstaller> _logger;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
|
||||
@@ -29,7 +30,7 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
IEventAggregator eventAggregator,
|
||||
IOptions<UnattendedSettings> unattendedSettings,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IDbProviderFactoryCreator dbProviderFactoryCreator,
|
||||
ILogger<UnattendedInstaller> logger,
|
||||
IRuntimeState runtimeState)
|
||||
{
|
||||
@@ -37,7 +38,7 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||
_unattendedSettings = unattendedSettings;
|
||||
_databaseFactory = databaseFactory;
|
||||
_globalSettings = globalSettings;
|
||||
_dbProviderFactoryCreator = dbProviderFactoryCreator;
|
||||
_logger = logger;
|
||||
_runtimeState = runtimeState;
|
||||
}
|
||||
@@ -56,7 +57,11 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var tries = _globalSettings.Value.InstallMissingDatabase ? 2 : 5;
|
||||
_runtimeState.DetermineRuntimeLevel();
|
||||
if (_runtimeState.Reason == RuntimeLevelReason.InstallMissingDatabase)
|
||||
{
|
||||
_dbProviderFactoryCreator.CreateDatabase(_databaseFactory.ProviderName, _databaseFactory.ConnectionString);
|
||||
}
|
||||
|
||||
bool connect;
|
||||
try
|
||||
@@ -64,12 +69,13 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
for (var i = 0; ;)
|
||||
{
|
||||
connect = _databaseFactory.CanConnect;
|
||||
if (connect || ++i == tries)
|
||||
if (connect || ++i == 5)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Could not immediately connect to database, trying again.");
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Migrations;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -22,14 +21,13 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
{
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly IMigrationBuilder _migrationBuilder;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IKeyValueService _keyValueService;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ILogger<DatabaseBuilder> _logger;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator;
|
||||
private readonly IConfigManipulator _configManipulator;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IMigrationPlanExecutor _migrationPlanExecutor;
|
||||
private readonly DatabaseSchemaCreatorFactory _databaseSchemaCreatorFactory;
|
||||
|
||||
@@ -41,26 +39,25 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
public DatabaseBuilder(
|
||||
IScopeProvider scopeProvider,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
IRuntimeState runtime,
|
||||
IRuntimeState runtimeState,
|
||||
ILoggerFactory loggerFactory,
|
||||
IMigrationBuilder migrationBuilder,
|
||||
IKeyValueService keyValueService,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IDbProviderFactoryCreator dbProviderFactoryCreator,
|
||||
IConfigManipulator configManipulator,
|
||||
IOptionsMonitor<GlobalSettings> globalSettings,
|
||||
IOptionsMonitor<ConnectionStrings> connectionStrings,
|
||||
IMigrationPlanExecutor migrationPlanExecutor,
|
||||
DatabaseSchemaCreatorFactory databaseSchemaCreatorFactory)
|
||||
{
|
||||
_scopeProvider = scopeProvider;
|
||||
_databaseFactory = databaseFactory;
|
||||
_runtime = runtime;
|
||||
_runtimeState = runtimeState;
|
||||
_logger = loggerFactory.CreateLogger<DatabaseBuilder>();
|
||||
_loggerFactory = loggerFactory;
|
||||
_migrationBuilder = migrationBuilder;
|
||||
_keyValueService = keyValueService;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_dbProviderFactoryCreator = dbProviderFactoryCreator;
|
||||
_configManipulator = configManipulator;
|
||||
_globalSettings = globalSettings;
|
||||
_connectionStrings = connectionStrings;
|
||||
_migrationPlanExecutor = migrationPlanExecutor;
|
||||
_databaseSchemaCreatorFactory = databaseSchemaCreatorFactory;
|
||||
}
|
||||
@@ -84,15 +81,15 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
/// </summary>
|
||||
public bool CanConnect(string databaseType, string connectionString, string server, string database, string login, string password, bool integratedAuth)
|
||||
{
|
||||
// we do not test SqlCE connection
|
||||
if (databaseType.InvariantContains("sqlce"))
|
||||
// we do not test SqlCE or LocalDB connections
|
||||
if (databaseType.InvariantContains("SqlCe") || databaseType.InvariantContains("SqlLocalDb"))
|
||||
return true;
|
||||
|
||||
string providerName;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(connectionString) == false)
|
||||
{
|
||||
providerName = DbConnectionExtensions.DetectProviderNameFromConnectionString(connectionString);
|
||||
providerName = ConfigConnectionString.ParseProviderName(connectionString);
|
||||
}
|
||||
else if (integratedAuth)
|
||||
{
|
||||
@@ -146,30 +143,29 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
|
||||
#region Configure Connection String
|
||||
|
||||
public const string EmbeddedDatabaseConnectionString = @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1;";
|
||||
public const string EmbeddedDatabaseConnectionString = @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1";
|
||||
|
||||
/// <summary>
|
||||
/// Configures a connection string for the embedded database.
|
||||
/// </summary>
|
||||
public void ConfigureEmbeddedDatabaseConnection()
|
||||
{
|
||||
ConfigureEmbeddedDatabaseConnection(_databaseFactory);
|
||||
const string connectionString = EmbeddedDatabaseConnectionString;
|
||||
const string providerName = Constants.DbProviderNames.SqlCe;
|
||||
|
||||
_configManipulator.SaveConnectionString(connectionString, providerName);
|
||||
Configure(connectionString, providerName, true);
|
||||
}
|
||||
|
||||
private void ConfigureEmbeddedDatabaseConnection(IUmbracoDatabaseFactory factory)
|
||||
public const string LocalDbConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True";
|
||||
|
||||
public void ConfigureSqlLocalDbDatabaseConnection()
|
||||
{
|
||||
_configManipulator.SaveConnectionString(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe);
|
||||
string connectionString = LocalDbConnectionString;
|
||||
const string providerName = Constants.DbProviderNames.SqlServer;
|
||||
|
||||
var path = _hostingEnvironment.MapPathContentRoot(Path.Combine(Constants.SystemDirectories.Data, "Umbraco.sdf"));
|
||||
if (File.Exists(path) == false)
|
||||
{
|
||||
// this should probably be in a "using (new SqlCeEngine)" clause but not sure
|
||||
// of the side effects and it's been like this for quite some time now
|
||||
|
||||
_dbProviderFactoryCreator.CreateDatabase(Constants.DbProviderNames.SqlCe);
|
||||
}
|
||||
|
||||
factory.Configure(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe);
|
||||
_configManipulator.SaveConnectionString(connectionString, providerName);
|
||||
Configure(connectionString, providerName, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,10 +175,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
/// <remarks>Has to be SQL Server</remarks>
|
||||
public void ConfigureDatabaseConnection(string connectionString)
|
||||
{
|
||||
const string providerName = Constants.DbProviderNames.SqlServer;
|
||||
|
||||
_configManipulator.SaveConnectionString(connectionString, providerName);
|
||||
_databaseFactory.Configure(connectionString, providerName);
|
||||
_configManipulator.SaveConnectionString(connectionString, null);
|
||||
Configure(connectionString, null, _globalSettings.CurrentValue.InstallMissingDatabase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -198,7 +192,21 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
var connectionString = GetDatabaseConnectionString(server, databaseName, user, password, databaseProvider, out var providerName);
|
||||
|
||||
_configManipulator.SaveConnectionString(connectionString, providerName);
|
||||
_databaseFactory.Configure(connectionString, providerName);
|
||||
Configure(connectionString, providerName, _globalSettings.CurrentValue.InstallMissingDatabase);
|
||||
}
|
||||
|
||||
private void Configure(string connectionString, string providerName, bool installMissingDatabase)
|
||||
{
|
||||
// Update existing connection string
|
||||
var umbracoConnectionString = new ConfigConnectionString(Constants.System.UmbracoConnectionName, connectionString, providerName);
|
||||
_connectionStrings.CurrentValue.UmbracoConnectionString = umbracoConnectionString;
|
||||
|
||||
_databaseFactory.Configure(umbracoConnectionString.ConnectionString, umbracoConnectionString.ProviderName);
|
||||
|
||||
if (installMissingDatabase)
|
||||
{
|
||||
CreateDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -214,9 +222,10 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
public static string GetDatabaseConnectionString(string server, string databaseName, string user, string password, string databaseProvider, out string providerName)
|
||||
{
|
||||
providerName = Constants.DbProviderNames.SqlServer;
|
||||
var provider = databaseProvider.ToLower();
|
||||
if (provider.InvariantContains("azure"))
|
||||
|
||||
if (databaseProvider.InvariantContains("Azure"))
|
||||
return GetAzureConnectionString(server, databaseName, user, password);
|
||||
|
||||
return $"server={server};database={databaseName};user id={user};password={password}";
|
||||
}
|
||||
|
||||
@@ -228,8 +237,10 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
public void ConfigureIntegratedSecurityDatabaseConnection(string server, string databaseName)
|
||||
{
|
||||
var connectionString = GetIntegratedSecurityDatabaseConnectionString(server, databaseName);
|
||||
_configManipulator.SaveConnectionString(connectionString, Constants.DbProviderNames.SqlServer);
|
||||
_databaseFactory.Configure(connectionString, Constants.DbProviderNames.SqlServer);
|
||||
const string providerName = Constants.DbProviderNames.SqlServer;
|
||||
|
||||
_configManipulator.SaveConnectionString(connectionString, providerName);
|
||||
_databaseFactory.Configure(connectionString, providerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -292,18 +303,14 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
return $"Server={server};Database={databaseName};User ID={user};Password={password}";
|
||||
}
|
||||
|
||||
private static bool ServerStartsWithTcp(string server)
|
||||
{
|
||||
return server.ToLower().StartsWith("tcp:".ToLower());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool ServerStartsWithTcp(string server) => server.InvariantStartsWith("tcp:");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Database Schema
|
||||
|
||||
public void CreateDatabase() => _dbProviderFactoryCreator.CreateDatabase(_databaseFactory.ProviderName, _databaseFactory.ConnectionString);
|
||||
|
||||
/// <summary>
|
||||
/// Validates the database schema.
|
||||
/// </summary>
|
||||
@@ -375,7 +382,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
//If the determined version is "empty" its a new install - otherwise upgrade the existing
|
||||
if (!hasInstalledVersion)
|
||||
{
|
||||
if (_runtime.Level == RuntimeLevel.Run)
|
||||
if (_runtimeState.Level == RuntimeLevel.Run)
|
||||
throw new Exception("Umbraco is already configured!");
|
||||
|
||||
var creator = _databaseSchemaCreatorFactory.Create(database);
|
||||
|
||||
@@ -1,35 +1,17 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StackExchange.Profiling.Data;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.FaultHandling;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
public static class DbConnectionExtensions
|
||||
{
|
||||
public static string DetectProviderNameFromConnectionString(string connectionString)
|
||||
public static bool IsConnectionAvailable(string connectionString, DbProviderFactory factory)
|
||||
{
|
||||
var builder = new DbConnectionStringBuilder { ConnectionString = connectionString };
|
||||
var allKeys = builder.Keys.Cast<string>();
|
||||
|
||||
if (allKeys.InvariantContains("Data Source")
|
||||
//this dictionary is case insensitive
|
||||
&& builder["Data source"].ToString().InvariantContains(".sdf"))
|
||||
{
|
||||
return Cms.Core.Constants.DbProviderNames.SqlCe;
|
||||
}
|
||||
|
||||
return Cms.Core.Constants.DbProviderNames.SqlServer;
|
||||
}
|
||||
|
||||
public static bool IsConnectionAvailable(string connectionString, DbProviderFactory factory)
|
||||
{
|
||||
|
||||
var connection = factory?.CreateConnection();
|
||||
|
||||
if (connection == null)
|
||||
@@ -42,7 +24,6 @@ namespace Umbraco.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static bool IsAvailable(this IDbConnection connection)
|
||||
{
|
||||
try
|
||||
@@ -68,17 +49,25 @@ namespace Umbraco.Extensions
|
||||
internal static IDbConnection UnwrapUmbraco(this IDbConnection connection)
|
||||
{
|
||||
var unwrapped = connection;
|
||||
|
||||
IDbConnection c;
|
||||
do
|
||||
{
|
||||
c = unwrapped;
|
||||
if (unwrapped is ProfiledDbConnection profiled) unwrapped = profiled.WrappedConnection;
|
||||
if (unwrapped is RetryDbConnection retrying) unwrapped = retrying.Inner;
|
||||
|
||||
} while (c != unwrapped);
|
||||
if (unwrapped is ProfiledDbConnection profiled)
|
||||
{
|
||||
unwrapped = profiled.WrappedConnection;
|
||||
}
|
||||
|
||||
if (unwrapped is RetryDbConnection retrying)
|
||||
{
|
||||
unwrapped = retrying.Inner;
|
||||
}
|
||||
}
|
||||
while (c != unwrapped);
|
||||
|
||||
return unwrapped;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
@@ -11,7 +10,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
public class DbProviderFactoryCreator : IDbProviderFactoryCreator
|
||||
{
|
||||
private readonly Func<string, DbProviderFactory> _getFactory;
|
||||
private readonly IDictionary<string, IEmbeddedDatabaseCreator> _embeddedDatabaseCreators;
|
||||
private readonly IDictionary<string, IDatabaseCreator> _databaseCreators;
|
||||
private readonly IDictionary<string, ISqlSyntaxProvider> _syntaxProviders;
|
||||
private readonly IDictionary<string, IBulkSqlInsertProvider> _bulkSqlInsertProviders;
|
||||
private readonly IDictionary<string, IProviderSpecificMapperFactory> _providerSpecificMapperFactories;
|
||||
@@ -20,11 +19,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
Func<string, DbProviderFactory> getFactory,
|
||||
IEnumerable<ISqlSyntaxProvider> syntaxProviders,
|
||||
IEnumerable<IBulkSqlInsertProvider> bulkSqlInsertProviders,
|
||||
IEnumerable<IEmbeddedDatabaseCreator> embeddedDatabaseCreators,
|
||||
IEnumerable<IDatabaseCreator> databaseCreators,
|
||||
IEnumerable<IProviderSpecificMapperFactory> providerSpecificMapperFactories)
|
||||
{
|
||||
_getFactory = getFactory;
|
||||
_embeddedDatabaseCreators = embeddedDatabaseCreators.ToDictionary(x => x.ProviderName);
|
||||
_databaseCreators = databaseCreators.ToDictionary(x => x.ProviderName);
|
||||
_syntaxProviders = syntaxProviders.ToDictionary(x => x.ProviderName);
|
||||
_bulkSqlInsertProviders = bulkSqlInsertProviders.ToDictionary(x => x.ProviderName);
|
||||
_providerSpecificMapperFactories = providerSpecificMapperFactories.ToDictionary(x => x.ProviderName);
|
||||
@@ -60,11 +59,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
return result;
|
||||
}
|
||||
|
||||
public void CreateDatabase(string providerName)
|
||||
public void CreateDatabase(string providerName, string connectionString)
|
||||
{
|
||||
if (_embeddedDatabaseCreators.TryGetValue(providerName, out var creator))
|
||||
if (_databaseCreators.TryGetValue(providerName, out var creator))
|
||||
{
|
||||
creator.Create();
|
||||
creator.Create(connectionString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
public interface IDatabaseCreator
|
||||
{
|
||||
string ProviderName { get; }
|
||||
|
||||
void Create(string connectionString);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
DbProviderFactory CreateFactory(string providerName);
|
||||
ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName);
|
||||
IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName);
|
||||
void CreateDatabase(string providerName);
|
||||
void CreateDatabase(string providerName, string connectionString);
|
||||
NPocoMapperCollection ProviderSpecificMappers(string providerName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
public interface IEmbeddedDatabaseCreator
|
||||
{
|
||||
string ProviderName { get; }
|
||||
string ConnectionString { get; set; }
|
||||
void Create();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
public class NoopEmbeddedDatabaseCreator : IEmbeddedDatabaseCreator
|
||||
{
|
||||
public string ProviderName => Cms.Core.Constants.DatabaseProviders.SqlServer;
|
||||
|
||||
public string ConnectionString { get; set; }
|
||||
|
||||
public void Create()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
public class SqlServerDatabaseCreator : IDatabaseCreator
|
||||
{
|
||||
public string ProviderName => Constants.DatabaseProviders.SqlServer;
|
||||
|
||||
public void Create(string connectionString)
|
||||
{
|
||||
var builder = new SqlConnectionStringBuilder(connectionString);
|
||||
|
||||
// Get connection string without database specific information
|
||||
var masterBuilder = new SqlConnectionStringBuilder(builder.ConnectionString)
|
||||
{
|
||||
AttachDBFilename = string.Empty,
|
||||
InitialCatalog = string.Empty
|
||||
};
|
||||
var masterConnectionString = masterBuilder.ConnectionString;
|
||||
|
||||
string fileName = builder.AttachDBFilename,
|
||||
database = builder.InitialCatalog;
|
||||
|
||||
// Create database
|
||||
if (!string.IsNullOrEmpty(fileName) && !File.Exists(fileName))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(database))
|
||||
{
|
||||
// Use a temporary database name
|
||||
database = "Umbraco-" + Guid.NewGuid();
|
||||
}
|
||||
|
||||
using var connection = new SqlConnection(masterConnectionString);
|
||||
connection.Open();
|
||||
|
||||
using var command = new SqlCommand(
|
||||
$"CREATE DATABASE [{database}] ON (NAME='{database}', FILENAME='{fileName}');" +
|
||||
$"ALTER DATABASE [{database}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;" +
|
||||
$"EXEC sp_detach_db @dbname='{database}';",
|
||||
connection);
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
connection.Close();
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(database))
|
||||
{
|
||||
using var connection = new SqlConnection(masterConnectionString);
|
||||
connection.Open();
|
||||
|
||||
using var command = new SqlCommand(
|
||||
$"IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = '{database}') " +
|
||||
$"CREATE DATABASE [{database}];",
|
||||
connection);
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,12 @@ using System.Data.Common;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NPoco;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
[Obsolete("This is only used for integration tests and should be moved into a test project.")]
|
||||
public class SqlServerDbProviderFactoryCreator : IDbProviderFactoryCreator
|
||||
{
|
||||
private readonly Func<string, DbProviderFactory> _getFactory;
|
||||
@@ -23,35 +23,29 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
public DbProviderFactory CreateFactory(string providerName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(providerName)) return null;
|
||||
|
||||
return _getFactory(providerName);
|
||||
}
|
||||
|
||||
// gets the sql syntax provider that corresponds, from attribute
|
||||
public ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName)
|
||||
{
|
||||
return providerName switch
|
||||
=> providerName switch
|
||||
{
|
||||
Cms.Core.Constants.DbProviderNames.SqlCe => throw new NotSupportedException("SqlCe is not supported"),
|
||||
Cms.Core.Constants.DbProviderNames.SqlServer => new SqlServerSyntaxProvider(_globalSettings),
|
||||
_ => throw new InvalidOperationException($"Unknown provider name \"{providerName}\""),
|
||||
};
|
||||
}
|
||||
|
||||
public IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName)
|
||||
{
|
||||
switch (providerName)
|
||||
=> providerName switch
|
||||
{
|
||||
case Cms.Core.Constants.DbProviderNames.SqlCe:
|
||||
throw new NotSupportedException("SqlCe is not supported");
|
||||
case Cms.Core.Constants.DbProviderNames.SqlServer:
|
||||
return new SqlServerBulkSqlInsertProvider();
|
||||
default:
|
||||
return new BasicBulkSqlInsertProvider();
|
||||
}
|
||||
}
|
||||
Cms.Core.Constants.DbProviderNames.SqlCe => throw new NotSupportedException("SqlCe is not supported"),
|
||||
Cms.Core.Constants.DbProviderNames.SqlServer => new SqlServerBulkSqlInsertProvider(),
|
||||
_ => new BasicBulkSqlInsertProvider(),
|
||||
};
|
||||
|
||||
public void CreateDatabase(string providerName)
|
||||
=> throw new NotSupportedException("Embedded databases are not supported");
|
||||
public void CreateDatabase(string providerName, string connectionString)
|
||||
=> throw new NotSupportedException("Embedded databases are not supported"); // TODO But LocalDB is?
|
||||
|
||||
public NPocoMapperCollection ProviderSpecificMappers(string providerName)
|
||||
=> new NPocoMapperCollection(() => Enumerable.Empty<IMapper>());
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NPoco;
|
||||
using StackExchange.Profiling;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.FaultHandling;
|
||||
using Umbraco.Extensions;
|
||||
@@ -62,6 +61,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
_connectionRetryPolicy = connectionRetryPolicy;
|
||||
_commandRetryPolicy = commandRetryPolicy;
|
||||
_mapperCollection = mapperCollection;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
SqlContext = sqlContext;
|
||||
_logger = logger;
|
||||
_bulkSqlInsertProvider = bulkSqlInsertProvider;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
@@ -86,6 +87,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
EnableSqlTrace = EnableSqlTraceDefault;
|
||||
NPocoDatabaseExtensions.ConfigureNPocoBulkExtensions();
|
||||
|
||||
if (_mapperCollection != null)
|
||||
{
|
||||
Mappers.AddRange(_mapperCollection);
|
||||
@@ -104,11 +106,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
var command = base.CreateCommand(connection, commandType, sql, args);
|
||||
|
||||
if (!DatabaseType.IsSqlCe()) return command;
|
||||
if (!DatabaseType.IsSqlCe())
|
||||
return command;
|
||||
|
||||
foreach (DbParameter parameter in command.Parameters)
|
||||
{
|
||||
if (parameter.Value == DBNull.Value)
|
||||
parameter.DbType = DbType.String;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
@@ -128,17 +133,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public string InstanceId
|
||||
{
|
||||
get
|
||||
{
|
||||
public string InstanceId =>
|
||||
#if DEBUG_DATABASES
|
||||
return _instanceGuid.ToString("N").Substring(0, 8) + ':' + _spid;
|
||||
_instanceGuid.ToString("N").Substring(0, 8) + ':' + _spid;
|
||||
#else
|
||||
return _instanceId ?? (_instanceId = _instanceGuid.ToString("N").Substring(0, 8));
|
||||
_instanceId ??= _instanceGuid.ToString("N").Substring(0, 8);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool InTransaction { get; private set; }
|
||||
@@ -175,8 +175,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
set
|
||||
{
|
||||
_enableCount = value;
|
||||
|
||||
if (_enableCount == false)
|
||||
{
|
||||
SqlCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,11 +196,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
|
||||
internal IEnumerable<CommandInfo> Commands => _commands;
|
||||
|
||||
public int BulkInsertRecords<T>(IEnumerable<T> records)
|
||||
{
|
||||
return _bulkSqlInsertProvider.BulkInsertRecords(this, records);
|
||||
|
||||
}
|
||||
public int BulkInsertRecords<T>(IEnumerable<T> records) => _bulkSqlInsertProvider.BulkInsertRecords(this, records);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="DatabaseSchemaResult"/> for the database
|
||||
@@ -206,6 +205,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
var dbSchema = _databaseSchemaCreatorFactory.Create(this);
|
||||
var databaseSchemaValidationResult = dbSchema.ValidateSchema();
|
||||
|
||||
return databaseSchemaValidationResult;
|
||||
}
|
||||
|
||||
@@ -263,8 +263,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
_logger.LogError(ex, "Exception ({InstanceId}).", InstanceId);
|
||||
_logger.LogDebug("At:\r\n{StackTrace}", Environment.StackTrace);
|
||||
|
||||
if (EnableSqlTrace == false)
|
||||
_logger.LogDebug("Sql:\r\n{Sql}", CommandToString(LastSQL, LastArgs));
|
||||
|
||||
base.OnException(ex);
|
||||
}
|
||||
|
||||
@@ -287,22 +289,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
#endif
|
||||
|
||||
_cmd = cmd;
|
||||
|
||||
base.OnExecutingCommand(cmd);
|
||||
}
|
||||
|
||||
private string CommandToString(DbCommand cmd)
|
||||
{
|
||||
return CommandToString(cmd.CommandText, cmd.Parameters.Cast<DbParameter>().Select(x => x.Value).ToArray());
|
||||
}
|
||||
private string CommandToString(DbCommand cmd) => CommandToString(cmd.CommandText, cmd.Parameters.Cast<DbParameter>().Select(x => x.Value).ToArray());
|
||||
|
||||
private string CommandToString(string sql, object[] args)
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
#if DEBUG_DATABASES
|
||||
text.Append(InstanceId);
|
||||
text.Append(": ");
|
||||
text.Append(InstanceId);
|
||||
text.Append(": ");
|
||||
#endif
|
||||
|
||||
NPocoSqlExtensions.ToText(sql, args, text);
|
||||
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
@@ -325,11 +327,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
Text = cmd.CommandText;
|
||||
var parameters = new List<ParameterInfo>();
|
||||
foreach (IDbDataParameter parameter in cmd.Parameters) parameters.Add(new ParameterInfo(parameter));
|
||||
foreach (IDbDataParameter parameter in cmd.Parameters)
|
||||
parameters.Add(new ParameterInfo(parameter));
|
||||
|
||||
Parameters = parameters.ToArray();
|
||||
}
|
||||
|
||||
public string Text { get; }
|
||||
|
||||
public ParameterInfo[] Parameters { get; }
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
_loggerFactory = loggerFactory;
|
||||
|
||||
var settings = connectionStrings.CurrentValue.UmbracoConnectionString;
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
logger.LogDebug("Missing connection string, defer configuration.");
|
||||
@@ -105,9 +104,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
|
||||
// could as well be <add name="umbracoDbDSN" connectionString="" providerName="" />
|
||||
// so need to test the values too
|
||||
var connectionString = settings.ConnectionString;
|
||||
var providerName = settings.ProviderName;
|
||||
if (string.IsNullOrWhiteSpace(connectionString) || string.IsNullOrWhiteSpace(providerName))
|
||||
if (settings.IsConnectionStringConfigured() == false)
|
||||
{
|
||||
logger.LogDebug("Empty connection string or provider name, defer configuration.");
|
||||
return; // not configured
|
||||
@@ -148,7 +145,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
private void UpdateSqlServerDatabaseType()
|
||||
{
|
||||
// replace NPoco database type by a more efficient one
|
||||
|
||||
var setting = _globalSettings.Value.DatabaseFactoryServerVersion;
|
||||
var fromSettings = false;
|
||||
|
||||
@@ -188,6 +184,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
// must be initialized to have a context
|
||||
EnsureInitialized();
|
||||
|
||||
return _sqlContext;
|
||||
}
|
||||
}
|
||||
@@ -199,15 +196,13 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
{
|
||||
// must be initialized to have a bulk insert provider
|
||||
EnsureInitialized();
|
||||
|
||||
return _bulkSqlInsertProvider;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ConfigureForUpgrade()
|
||||
{
|
||||
_upgrading = true;
|
||||
}
|
||||
public void ConfigureForUpgrade() => _upgrading = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Configure(string connectionString, string providerName)
|
||||
@@ -248,13 +243,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
throw new Exception($"Can't find a provider factory for provider name \"{_providerName}\".");
|
||||
}
|
||||
|
||||
// cannot initialize without being able to talk to the database
|
||||
// TODO: Why not?
|
||||
if (!DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DbProviderFactory))
|
||||
{
|
||||
throw new Exception("Cannot connect to the database.");
|
||||
}
|
||||
|
||||
_connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(ConnectionString);
|
||||
_commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(ConnectionString);
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ using Umbraco.Cms.Core.Logging;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Runtime;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -96,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();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -13,6 +12,7 @@ using Umbraco.Cms.Core.Semver;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Runtime
|
||||
{
|
||||
@@ -110,7 +110,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)
|
||||
if (_globalSettings.Value.InstallMissingDatabase || CanAutoInstallMissingDatabase(_databaseFactory))
|
||||
{
|
||||
// ok to install on a configured but missing database
|
||||
Level = RuntimeLevel.Install;
|
||||
@@ -173,6 +173,17 @@ namespace Umbraco.Cms.Infrastructure.Runtime
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(RuntimeLevel level, RuntimeLevelReason reason, Exception bootFailedException = null)
|
||||
{
|
||||
Level = level;
|
||||
Reason = reason;
|
||||
|
||||
if (bootFailedException != null)
|
||||
{
|
||||
BootFailedException = new BootFailedException(bootFailedException.Message, bootFailedException);
|
||||
}
|
||||
}
|
||||
|
||||
private enum UmbracoDatabaseState
|
||||
{
|
||||
Ok,
|
||||
@@ -233,17 +244,6 @@ namespace Umbraco.Cms.Infrastructure.Runtime
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(RuntimeLevel level, RuntimeLevelReason reason, Exception bootFailedException = null)
|
||||
{
|
||||
Level = level;
|
||||
Reason = reason;
|
||||
|
||||
if (bootFailedException != null)
|
||||
{
|
||||
BootFailedException = new BootFailedException(bootFailedException.Message, bootFailedException);
|
||||
}
|
||||
}
|
||||
|
||||
private bool DoesUmbracoRequireUpgrade(IReadOnlyDictionary<string, string> keyValues)
|
||||
{
|
||||
var upgrader = new Upgrader(new UmbracoPlan(_umbracoVersion));
|
||||
@@ -277,6 +277,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime
|
||||
return canConnect;
|
||||
}
|
||||
|
||||
|
||||
private bool CanAutoInstallMissingDatabase(IUmbracoDatabaseFactory databaseFactory)
|
||||
=> databaseFactory.ProviderName == Constants.DatabaseProviders.SqlCe ||
|
||||
databaseFactory.ConnectionString?.InvariantContains("(localdb)") == true;
|
||||
}
|
||||
}
|
||||
|
||||
16
src/Umbraco.Persistence.SqlCe/SqlCeDatabaseCreator.cs
Normal file
16
src/Umbraco.Persistence.SqlCe/SqlCeDatabaseCreator.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Persistence.SqlCe
|
||||
{
|
||||
public class SqlCeDatabaseCreator : IDatabaseCreator
|
||||
{
|
||||
public string ProviderName => Constants.DatabaseProviders.SqlCe;
|
||||
|
||||
public void Create(string connectionString)
|
||||
{
|
||||
using var engine = new System.Data.SqlServerCe.SqlCeEngine(connectionString);
|
||||
engine.CreateDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Persistence.SqlCe
|
||||
{
|
||||
public class SqlCeEmbeddedDatabaseCreator : IEmbeddedDatabaseCreator
|
||||
{
|
||||
public string ProviderName => Constants.DatabaseProviders.SqlCe;
|
||||
|
||||
public string ConnectionString { get; set; } = DatabaseBuilder.EmbeddedDatabaseConnectionString;
|
||||
public void Create()
|
||||
{
|
||||
var engine = new System.Data.SqlServerCe.SqlCeEngine(ConnectionString);
|
||||
engine.CreateDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence
|
||||
{
|
||||
private IDbProviderFactoryCreator DbProviderFactoryCreator => GetRequiredService<IDbProviderFactoryCreator>();
|
||||
private IUmbracoDatabaseFactory UmbracoDatabaseFactory => GetRequiredService<IUmbracoDatabaseFactory>();
|
||||
private IEmbeddedDatabaseCreator EmbeddedDatabaseCreator => GetRequiredService<IEmbeddedDatabaseCreator>();
|
||||
private IDatabaseCreator EmbeddedDatabaseCreator => GetRequiredService<IDatabaseCreator>();
|
||||
|
||||
public DatabaseBuilderTests()
|
||||
{
|
||||
@@ -42,10 +42,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence
|
||||
if (File.Exists(filePath))
|
||||
File.Delete(filePath);
|
||||
|
||||
EmbeddedDatabaseCreator.ConnectionString = $"Datasource=|DataDirectory|{dbFile};Flush Interval=1";
|
||||
var connectionString = $"Datasource=|DataDirectory|{dbFile};Flush Interval=1";
|
||||
|
||||
UmbracoDatabaseFactory.Configure(EmbeddedDatabaseCreator.ConnectionString, Constants.DbProviderNames.SqlCe);
|
||||
DbProviderFactoryCreator.CreateDatabase(Constants.DbProviderNames.SqlCe);
|
||||
UmbracoDatabaseFactory.Configure(connectionString, Constants.DbProviderNames.SqlCe);
|
||||
DbProviderFactoryCreator.CreateDatabase(Constants.DbProviderNames.SqlCe, connectionString);
|
||||
UmbracoDatabaseFactory.CreateDatabase();
|
||||
|
||||
// test get database type (requires an actual database)
|
||||
|
||||
@@ -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
|
||||
@@ -154,7 +157,7 @@ namespace Umbraco.Extensions
|
||||
DbProviderFactories.GetFactory,
|
||||
factory.GetServices<ISqlSyntaxProvider>(),
|
||||
factory.GetServices<IBulkSqlInsertProvider>(),
|
||||
factory.GetServices<IEmbeddedDatabaseCreator>(),
|
||||
factory.GetServices<IDatabaseCreator>(),
|
||||
factory.GetServices<IProviderSpecificMapperFactory>()
|
||||
));
|
||||
|
||||
@@ -361,17 +364,17 @@ namespace Umbraco.Extensions
|
||||
|
||||
Type sqlCeSyntaxProviderType = umbSqlCeAssembly.GetType("Umbraco.Cms.Persistence.SqlCe.SqlCeSyntaxProvider");
|
||||
Type sqlCeBulkSqlInsertProviderType = umbSqlCeAssembly.GetType("Umbraco.Cms.Persistence.SqlCe.SqlCeBulkSqlInsertProvider");
|
||||
Type sqlCeEmbeddedDatabaseCreatorType = umbSqlCeAssembly.GetType("Umbraco.Cms.Persistence.SqlCe.SqlCeEmbeddedDatabaseCreator");
|
||||
Type sqlCeDatabaseCreatorType = umbSqlCeAssembly.GetType("Umbraco.Cms.Persistence.SqlCe.SqlCeDatabaseCreator");
|
||||
Type sqlCeSpecificMapperFactory = umbSqlCeAssembly.GetType("Umbraco.Cms.Persistence.SqlCe.SqlCeSpecificMapperFactory");
|
||||
|
||||
if (!(sqlCeSyntaxProviderType is null
|
||||
|| sqlCeBulkSqlInsertProviderType is null
|
||||
|| sqlCeEmbeddedDatabaseCreatorType is null
|
||||
|| sqlCeDatabaseCreatorType is null
|
||||
|| sqlCeSpecificMapperFactory is null))
|
||||
{
|
||||
builder.Services.AddSingleton(typeof(ISqlSyntaxProvider), sqlCeSyntaxProviderType);
|
||||
builder.Services.AddSingleton(typeof(IBulkSqlInsertProvider), sqlCeBulkSqlInsertProviderType);
|
||||
builder.Services.AddSingleton(typeof(IEmbeddedDatabaseCreator), sqlCeEmbeddedDatabaseCreatorType);
|
||||
builder.Services.AddSingleton(typeof(IDatabaseCreator), sqlCeDatabaseCreatorType);
|
||||
builder.Services.AddSingleton(typeof(IProviderSpecificMapperFactory), sqlCeSpecificMapperFactory);
|
||||
}
|
||||
|
||||
@@ -401,7 +404,7 @@ namespace Umbraco.Extensions
|
||||
|
||||
builder.Services.AddSingleton<ISqlSyntaxProvider, SqlServerSyntaxProvider>();
|
||||
builder.Services.AddSingleton<IBulkSqlInsertProvider, SqlServerBulkSqlInsertProvider>();
|
||||
builder.Services.AddSingleton<IEmbeddedDatabaseCreator, NoopEmbeddedDatabaseCreator>();
|
||||
builder.Services.AddSingleton<IDatabaseCreator, SqlServerDatabaseCreator>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -10,38 +10,48 @@
|
||||
<legend>What type of database do you use?</legend>
|
||||
<label class="control-label" for="dbType">Database type</label>
|
||||
<div class="controls">
|
||||
<select id="dbType" name="dbType"
|
||||
<select id="dbType"
|
||||
ng-options="db.id as db.name for db in dbs"
|
||||
required ng-model="installer.current.model.dbType">
|
||||
required
|
||||
ng-model="installer.current.model.dbType">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls" ng-if="installer.current.model.dbType == 'SqlCe' ">
|
||||
<div class="controls" ng-if="installer.current.model.dbType == 'SqlLocalDb'">
|
||||
<p>Great! No need to configure anything, you can simply click the <strong>continue</strong> button below to continue to the next step</p>
|
||||
</div>
|
||||
|
||||
<div ng-if="installer.current.model.dbType == 'Custom' ">
|
||||
<div class="controls" ng-if="installer.current.model.dbType == 'SqlCe'">
|
||||
<p>Great! No need to configure anything, you can simply click the <strong>continue</strong> button below to continue to the next step</p>
|
||||
</div>
|
||||
|
||||
<div ng-if="installer.current.model.dbType == 'Custom'">
|
||||
<legend>What is the exact connection string we should use?</legend>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="server">Connection string</label>
|
||||
<label class="control-label" for="Custom_connectionString">Connection string</label>
|
||||
<div class="controls">
|
||||
<textarea class="input-block-level" required ng-model="installer.current.model.connectionString" rows="5"></textarea>
|
||||
<textarea id="Custom_connectionString"
|
||||
class="input-block-level"
|
||||
required
|
||||
ng-model="installer.current.model.connectionString"
|
||||
rows="5"></textarea>
|
||||
<small class="inline-help">Enter a valid database connection string.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="installer.current.model.dbType == 'SqlAzure' || installer.current.model.dbType == 'SqlServer' ">
|
||||
<div ng-if="installer.current.model.dbType == 'SqlAzure' || installer.current.model.dbType == 'SqlServer'">
|
||||
<div class="row">
|
||||
<legend>Where do we find your database?</legend>
|
||||
<div class="span6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="server">Server</label>
|
||||
<label class="control-label" for="Sql_Server">Server</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="server" name="server"
|
||||
placeholder="{{ (installer.current.model.dbType == 'SqlAzure') ? 'umbraco-database.database.windows.net' : '127.0.0.1\\SQLEXPRESS'}}"
|
||||
required ng-model="installer.current.model.server" />
|
||||
<input type="text" id="Sql_Server"
|
||||
placeholder="{{ (installer.current.model.dbType == 'SqlAzure') ? 'umbraco-database.database.windows.net' : '(local)\\SQLEXPRESS'}}"
|
||||
required
|
||||
ng-model="installer.current.model.server" />
|
||||
<small class="inline-help">Enter server domain or IP</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,11 +59,12 @@
|
||||
|
||||
<div class="span6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="databaseName">Database name</label>
|
||||
<label class="control-label" for="Sql_databaseName">Database name</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="databaseName" name="installer.current.model.databaseName"
|
||||
<input type="text" id="Sql_databaseName"
|
||||
placeholder="umbraco-cms"
|
||||
required ng-model="installer.current.model.databaseName" />
|
||||
required
|
||||
ng-model="installer.current.model.databaseName" />
|
||||
<small class="inline-help">Enter the name of the database</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,11 +75,12 @@
|
||||
<legend>What credentials are used to access the database?</legend>
|
||||
<div class="span6">
|
||||
<div class="control-group" ng-if="!installer.current.model.integratedAuth">
|
||||
<label class="control-label" for="login">Login</label>
|
||||
<label class="control-label" for="Sql_login">Login</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="login" name="login"
|
||||
<input type="text" id="Sql_login"
|
||||
placeholder="umbraco-db-user"
|
||||
required ng-model="installer.current.model.login" />
|
||||
required
|
||||
ng-model="installer.current.model.login" />
|
||||
<small class="inline-help">Enter the database user name</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,21 +88,21 @@
|
||||
|
||||
<div class="span6">
|
||||
<div class="control-group" ng-if="!installer.current.model.integratedAuth">
|
||||
<label class="control-label" for="password">Password</label>
|
||||
<label class="control-label" for="Sql_password">Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" id="password" name="password"
|
||||
<input type="password" id="Sql_password"
|
||||
placeholder="umbraco-db-password"
|
||||
required ng-model="installer.current.model.password" />
|
||||
required
|
||||
ng-model="installer.current.model.password" />
|
||||
<small class="inline-help">Enter the database password</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span12 control-group" ng-if="installer.current.model.dbType =='SqlServer' ">
|
||||
<div class="span12 control-group" ng-if="installer.current.model.dbType == 'SqlServer'">
|
||||
<div class="controls">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="integratedAuth" name="integratedAuth"
|
||||
ng-model="installer.current.model.integratedAuth" />
|
||||
<input type="checkbox" ng-model="installer.current.model.integratedAuth" />
|
||||
Use integrated authentication
|
||||
</label>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user