Add LocalDB database option to installer
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Install.Models
|
||||
{
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Cms.Core.Install.Models
|
||||
public DatabaseModel()
|
||||
{
|
||||
//defaults
|
||||
DatabaseType = DatabaseType.SqlCe;
|
||||
DatabaseType = DatabaseType.SqlLocalDb;
|
||||
}
|
||||
|
||||
[DataMember(Name = "dbType")]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace Umbraco.Cms.Core.Install.Models
|
||||
namespace Umbraco.Cms.Core.Install.Models
|
||||
{
|
||||
public enum DatabaseType
|
||||
{
|
||||
SqlLocalDb,
|
||||
SqlCe,
|
||||
SqlServer,
|
||||
SqlAzure,
|
||||
|
||||
@@ -7,6 +7,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
|
||||
@@ -39,7 +40,9 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
throw new InstallException("Could not connect to the database");
|
||||
}
|
||||
|
||||
ConfigureConnection(database);
|
||||
|
||||
return Task.FromResult<InstallSetupResult>(null);
|
||||
}
|
||||
|
||||
@@ -49,6 +52,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 +69,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 +89,31 @@ 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);
|
||||
}
|
||||
!(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 +126,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,11 +1,14 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Install;
|
||||
using Umbraco.Cms.Core.Install.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
@@ -15,11 +18,13 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
private readonly DatabaseBuilder _databaseBuilder;
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
|
||||
public DatabaseInstallStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime)
|
||||
public DatabaseInstallStep(DatabaseBuilder databaseBuilder, IRuntimeState runtime, IOptionsMonitor<GlobalSettings> globalSettings)
|
||||
{
|
||||
_databaseBuilder = databaseBuilder;
|
||||
_runtime = runtime;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public override Task<InstallSetupResult> ExecuteAsync(object model)
|
||||
@@ -27,6 +32,11 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
if (_runtime.Level == RuntimeLevel.Run)
|
||||
throw new Exception("Umbraco is already configured!");
|
||||
|
||||
if (_globalSettings.CurrentValue.InstallMissingDatabase)
|
||||
{
|
||||
_databaseBuilder.CreateDatabase();
|
||||
}
|
||||
|
||||
var result = _databaseBuilder.CreateSchemaAndData();
|
||||
|
||||
if (result.Success == false)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
@@ -85,7 +83,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
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"))
|
||||
if (databaseType.InvariantContains("sqlce") || databaseType.InvariantContains("localdb"))
|
||||
return true;
|
||||
|
||||
string providerName;
|
||||
@@ -153,23 +151,32 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
/// </summary>
|
||||
public void ConfigureEmbeddedDatabaseConnection()
|
||||
{
|
||||
ConfigureEmbeddedDatabaseConnection(_databaseFactory);
|
||||
const string connectionString = EmbeddedDatabaseConnectionString;
|
||||
const string providerName = Constants.DbProviderNames.SqlCe;
|
||||
|
||||
_configManipulator.SaveConnectionString(connectionString, providerName);
|
||||
_databaseFactory.Configure(connectionString, providerName);
|
||||
|
||||
// Always create embedded database
|
||||
CreateDatabase();
|
||||
}
|
||||
|
||||
private void ConfigureEmbeddedDatabaseConnection(IUmbracoDatabaseFactory factory)
|
||||
public const string LocalDbConnectionString = @"Server=(localdb)\MSSQLLocalDB;Integrated Security=true;AttachDbFileName=|DataDirectory|\Umbraco.mdf";
|
||||
|
||||
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
|
||||
// Replace data directory placeholder (this is not supported by LocalDB)
|
||||
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
|
||||
connectionString = connectionString.Replace("|DataDirectory|", dataDirectory);
|
||||
|
||||
_dbProviderFactoryCreator.CreateDatabase(Constants.DbProviderNames.SqlCe);
|
||||
}
|
||||
_configManipulator.SaveConnectionString(connectionString, providerName);
|
||||
_databaseFactory.Configure(connectionString, providerName);
|
||||
|
||||
factory.Configure(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe);
|
||||
// Always create LocalDB database
|
||||
CreateDatabase();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -214,9 +221,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 +236,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 +302,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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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