2012-10-29 09:49:31 -01:00
using System ;
using System.Configuration ;
2012-11-26 17:58:42 -01:00
using System.Data.SqlServerCe ;
2012-11-27 10:05:26 -01:00
using System.IO ;
2012-11-26 15:57:03 -01:00
using System.Linq ;
2014-03-14 13:04:20 +11:00
using System.Web ;
2012-11-26 15:57:03 -01:00
using System.Web.Configuration ;
using System.Xml.Linq ;
2012-10-29 09:49:31 -01:00
using Umbraco.Core.Configuration ;
2013-10-04 10:53:22 +10:00
using Umbraco.Core.IO ;
2012-11-27 15:07:53 -01:00
using Umbraco.Core.Logging ;
2012-10-29 09:49:31 -01:00
using Umbraco.Core.Persistence ;
2012-11-30 18:48:20 -01:00
using Umbraco.Core.Persistence.Migrations ;
2013-01-25 15:05:42 -01:00
using Umbraco.Core.Persistence.Migrations.Initial ;
2012-10-29 09:49:31 -01:00
using Umbraco.Core.Persistence.SqlSyntax ;
namespace Umbraco.Core
{
/// <summary>
/// The Umbraco Database context
/// </summary>
/// <remarks>
/// One per AppDomain, represents the Umbraco database
/// </remarks>
public class DatabaseContext
{
2013-01-29 11:30:20 -01:00
private readonly IDatabaseFactory _factory ;
2015-01-09 13:00:26 +11:00
private readonly ILogger _logger ;
2015-01-13 18:19:52 +11:00
private readonly SqlSyntaxProviders _syntaxProviders ;
2013-01-29 11:30:20 -01:00
private bool _configured ;
2014-11-12 16:30:01 +11:00
private bool _canConnect ;
private volatile bool _connectCheck = false ;
private readonly object _locker = new object ( ) ;
2012-11-26 17:58:42 -01:00
private string _connectionString ;
2012-11-27 11:38:09 -01:00
private string _providerName ;
2013-01-28 14:01:38 -01:00
private DatabaseSchemaResult _result ;
2012-12-14 08:06:32 +05:00
2015-01-13 18:19:52 +11:00
/// <summary>
/// Default constructor
/// </summary>
/// <param name="factory"></param>
/// <param name="logger"></param>
/// <param name="syntaxProviders"></param>
public DatabaseContext ( IDatabaseFactory factory , ILogger logger , SqlSyntaxProviders syntaxProviders )
{
if ( factory = = null ) throw new ArgumentNullException ( "factory" ) ;
if ( logger = = null ) throw new ArgumentNullException ( "logger" ) ;
if ( syntaxProviders = = null ) throw new ArgumentNullException ( "syntaxProviders" ) ;
2015-01-09 13:35:32 +11:00
_factory = factory ;
_logger = logger ;
2015-01-13 18:19:52 +11:00
_syntaxProviders = syntaxProviders ;
2015-01-09 13:35:32 +11:00
}
2015-01-13 18:19:52 +11:00
/// <summary>
/// Create a configured DatabaseContext
/// </summary>
/// <param name="factory"></param>
/// <param name="logger"></param>
/// <param name="sqlSyntax"></param>
/// <param name="providerName"></param>
public DatabaseContext ( IDatabaseFactory factory , ILogger logger , ISqlSyntaxProvider sqlSyntax , string providerName )
2012-11-05 09:03:48 -01:00
{
2015-01-13 18:19:52 +11:00
_providerName = providerName ;
2015-01-09 13:00:26 +11:00
SqlSyntax = sqlSyntax ;
2013-01-29 11:30:20 -01:00
_factory = factory ;
2015-01-09 13:00:26 +11:00
_logger = logger ;
2015-01-13 18:19:52 +11:00
_configured = true ;
2012-11-05 09:03:48 -01:00
}
2012-12-12 03:47:04 +05:00
2015-01-09 13:00:26 +11:00
public ISqlSyntaxProvider SqlSyntax { get ; private set ; }
2012-10-29 09:49:31 -01:00
/// <summary>
/// Gets the <see cref="Database"/> object for doing CRUD operations
/// against custom tables that resides in the Umbraco database.
/// </summary>
/// <remarks>
/// This should not be used for CRUD operations or queries against the
2012-11-27 15:07:53 -01:00
/// standard Umbraco tables! Use the Public services for that.
2012-10-29 09:49:31 -01:00
/// </remarks>
2012-12-12 05:48:18 +05:00
public UmbracoDatabase Database
2012-10-29 09:49:31 -01:00
{
2012-12-12 03:47:04 +05:00
get { return _factory . CreateDatabase ( ) ; }
2012-10-29 09:49:31 -01:00
}
/// <summary>
/// Boolean indicating whether the database has been configured
/// </summary>
public bool IsDatabaseConfigured
{
get { return _configured ; }
}
2014-11-12 16:30:01 +11:00
/// <summary>
/// Determines if the db can be connected to
/// </summary>
public bool CanConnect
{
get
{
if ( IsDatabaseConfigured = = false ) return false ;
//double check lock so that it is only checked once and is fast
if ( _connectCheck = = false )
{
lock ( _locker )
{
if ( _canConnect = = false )
{
_canConnect = DbConnectionExtensions . IsConnectionAvailable ( ConnectionString , DatabaseProvider ) ;
2014-11-12 16:45:36 +11:00
_connectCheck = true ;
2014-11-12 16:30:01 +11:00
}
}
}
return _canConnect ;
}
}
2012-11-26 17:58:42 -01:00
/// <summary>
/// Gets the configured umbraco db connection string.
/// </summary>
public string ConnectionString
{
get { return _connectionString ; }
}
/// <summary>
/// Returns the name of the dataprovider from the connectionstring setting in config
/// </summary>
2012-11-30 17:56:02 -01:00
internal string ProviderName
2012-11-26 17:58:42 -01:00
{
get
{
2012-11-27 11:38:09 -01:00
if ( string . IsNullOrEmpty ( _providerName ) = = false )
return _providerName ;
_providerName = "System.Data.SqlClient" ;
2012-11-26 17:58:42 -01:00
if ( ConfigurationManager . ConnectionStrings [ GlobalSettings . UmbracoConnectionName ] ! = null )
{
2015-02-22 21:36:02 +01:00
if ( string . IsNullOrEmpty ( ConfigurationManager . ConnectionStrings [ GlobalSettings . UmbracoConnectionName ] . ProviderName ) = = false )
2012-11-27 11:38:09 -01:00
_providerName = ConfigurationManager . ConnectionStrings [ GlobalSettings . UmbracoConnectionName ] . ProviderName ;
2012-11-26 17:58:42 -01:00
}
else
{
throw new InvalidOperationException ( "Can't find a connection string with the name '" + GlobalSettings . UmbracoConnectionName + "'" ) ;
}
2012-11-27 11:38:09 -01:00
return _providerName ;
2012-11-26 17:58:42 -01:00
}
}
/// <summary>
/// Returns the Type of DatabaseProvider used
/// </summary>
public DatabaseProviders DatabaseProvider
{
get
{
string dbtype = Database . Connection = = null ? ProviderName : Database . Connection . GetType ( ) . Name ;
if ( dbtype . StartsWith ( "MySql" ) ) return DatabaseProviders . MySql ;
if ( dbtype . StartsWith ( "SqlCe" ) | | dbtype . Contains ( "SqlServerCe" ) ) return DatabaseProviders . SqlServerCE ;
if ( dbtype . StartsWith ( "Npgsql" ) ) return DatabaseProviders . PostgreSQL ;
if ( dbtype . StartsWith ( "Oracle" ) | | dbtype . Contains ( "OracleClient" ) ) return DatabaseProviders . Oracle ;
if ( dbtype . StartsWith ( "SQLite" ) ) return DatabaseProviders . SQLite ;
if ( dbtype . Contains ( "Azure" ) ) return DatabaseProviders . SqlAzure ;
return DatabaseProviders . SqlServer ;
}
}
2012-11-26 15:57:03 -01:00
/// <summary>
/// Configure a ConnectionString for the embedded database.
/// </summary>
2013-01-29 11:30:20 -01:00
public void ConfigureEmbeddedDatabaseConnection ( )
2012-11-26 15:57:03 -01:00
{
2012-12-31 12:15:46 -01:00
const string providerName = "System.Data.SqlServerCe.4.0" ;
2012-11-26 15:57:03 -01:00
2014-02-26 16:01:31 +01:00
var connectionString = GetEmbeddedDatabaseConnectionString ( ) ;
2012-12-31 12:15:46 -01:00
SaveConnectionString ( connectionString , providerName ) ;
2012-11-26 17:58:42 -01:00
2012-11-27 15:07:53 -01:00
var path = Path . Combine ( GlobalSettings . FullpathToRoot , "App_Data" , "Umbraco.sdf" ) ;
2012-11-27 10:05:26 -01:00
if ( File . Exists ( path ) = = false )
{
2014-03-04 16:40:23 +11:00
var engine = new SqlCeEngine ( connectionString ) ;
engine . CreateDatabase ( ) ;
// SD: Pretty sure this should be in a using clause but i don't want to cause unknown side-effects here
// since it's been like this for quite some time
//using (var engine = new SqlCeEngine(connectionString))
//{
// engine.CreateDatabase();
//}
2012-11-27 10:05:26 -01:00
}
2012-11-26 17:58:42 -01:00
2012-11-27 11:38:09 -01:00
Initialize ( providerName ) ;
2012-11-26 15:57:03 -01:00
}
2014-02-26 16:01:31 +01:00
public string GetEmbeddedDatabaseConnectionString ( )
{
return @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1;" ;
}
2012-11-26 15:57:03 -01:00
/// <summary>
/// Configure a ConnectionString that has been entered manually.
/// </summary>
/// <remarks>
/// Please note that we currently assume that the 'System.Data.SqlClient' provider can be used.
/// </remarks>
/// <param name="connectionString"></param>
public void ConfigureDatabaseConnection ( string connectionString )
{
2014-04-17 20:05:55 +10:00
var provider = DbConnectionExtensions . DetectProviderFromConnectionString ( connectionString ) ;
var databaseProvider = provider . ToString ( ) ;
var providerName = "System.Data.SqlClient" ;
if ( databaseProvider . ToLower ( ) . Contains ( "mysql" ) )
{
providerName = "MySql.Data.MySqlClient" ;
}
SaveConnectionString ( connectionString , providerName ) ;
2012-11-27 11:38:09 -01:00
Initialize ( string . Empty ) ;
2012-11-26 15:57:03 -01:00
}
/// <summary>
/// Configures a ConnectionString for the Umbraco database based on the passed in properties from the installer.
/// </summary>
/// <param name="server">Name or address of the database server</param>
/// <param name="databaseName">Name of the database</param>
/// <param name="user">Database Username</param>
/// <param name="password">Database Password</param>
/// <param name="databaseProvider">Type of the provider to be used (Sql, Sql Azure, Sql Ce, MySql)</param>
public void ConfigureDatabaseConnection ( string server , string databaseName , string user , string password , string databaseProvider )
2014-02-26 16:01:31 +01:00
{
string providerName ;
var connectionString = GetDatabaseConnectionString ( server , databaseName , user , password , databaseProvider , out providerName ) ;
SaveConnectionString ( connectionString , providerName ) ;
Initialize ( providerName ) ;
}
2014-04-17 20:05:55 +10:00
2014-02-26 16:01:31 +01:00
public string GetDatabaseConnectionString ( string server , string databaseName , string user , string password , string databaseProvider , out string providerName )
2012-11-26 15:57:03 -01:00
{
2014-02-26 16:01:31 +01:00
providerName = "System.Data.SqlClient" ;
2013-01-29 11:30:20 -01:00
if ( databaseProvider . ToLower ( ) . Contains ( "mysql" ) )
2012-11-26 15:57:03 -01:00
{
providerName = "MySql.Data.MySqlClient" ;
2014-02-26 16:01:31 +01:00
return string . Format ( "Server={0}; Database={1};Uid={2};Pwd={3}" , server , databaseName , user , password ) ;
2012-11-26 15:57:03 -01:00
}
2014-02-26 16:01:31 +01:00
if ( databaseProvider . ToLower ( ) . Contains ( "azure" ) )
2012-11-26 15:57:03 -01:00
{
2014-02-26 16:01:31 +01:00
return BuildAzureConnectionString ( server , databaseName , user , password ) ;
2012-11-26 15:57:03 -01:00
}
2014-02-26 16:01:31 +01:00
return string . Format ( "server={0};database={1};user id={2};password={3}" , server , databaseName , user , password ) ;
2012-11-26 15:57:03 -01:00
}
2013-07-19 04:26:50 -07:00
/// <summary>
/// Configures a ConnectionString for the Umbraco database that uses Microsoft SQL Server integrated security.
/// </summary>
/// <param name="server">Name or address of the database server</param>
/// <param name="databaseName">Name of the database</param>
public void ConfigureIntegratedSecurityDatabaseConnection ( string server , string databaseName )
{
const string providerName = "System.Data.SqlClient" ;
2014-02-26 16:01:31 +01:00
var connectionString = GetIntegratedSecurityDatabaseConnectionString ( server , databaseName ) ;
2013-07-19 04:26:50 -07:00
SaveConnectionString ( connectionString , providerName ) ;
Initialize ( providerName ) ;
}
2014-02-26 16:01:31 +01:00
public string GetIntegratedSecurityDatabaseConnectionString ( string server , string databaseName )
{
return String . Format ( "Server={0};Database={1};Integrated Security=true" , server , databaseName ) ;
}
2013-02-20 14:34:18 -01:00
internal string BuildAzureConnectionString ( string server , string databaseName , string user , string password )
{
if ( server . Contains ( "." ) & & ServerStartsWithTcp ( server ) = = false )
server = string . Format ( "tcp:{0}" , server ) ;
if ( server . Contains ( "." ) = = false & & ServerStartsWithTcp ( server ) )
{
string serverName = server . Contains ( "," )
? server . Substring ( 0 , server . IndexOf ( "," , StringComparison . Ordinal ) )
: server ;
var portAddition = string . Empty ;
if ( server . Contains ( "," ) )
portAddition = server . Substring ( server . IndexOf ( "," , StringComparison . Ordinal ) ) ;
server = string . Format ( "{0}.database.windows.net{1}" , serverName , portAddition ) ;
}
if ( ServerStartsWithTcp ( server ) = = false )
server = string . Format ( "tcp:{0}.database.windows.net" , server ) ;
if ( server . Contains ( "," ) = = false )
server = string . Format ( "{0},1433" , server ) ;
if ( user . Contains ( "@" ) = = false )
{
var userDomain = server ;
if ( ServerStartsWithTcp ( server ) )
userDomain = userDomain . Substring ( userDomain . IndexOf ( ":" , StringComparison . Ordinal ) + 1 ) ;
if ( userDomain . Contains ( "." ) )
userDomain = userDomain . Substring ( 0 , userDomain . IndexOf ( "." , StringComparison . Ordinal ) ) ;
user = string . Format ( "{0}@{1}" , user , userDomain ) ;
}
return string . Format ( "Server={0};Database={1};User ID={2};Password={3}" , server , databaseName , user , password ) ;
}
private static bool ServerStartsWithTcp ( string server )
{
return server . ToLower ( ) . StartsWith ( "tcp:" . ToLower ( ) ) ;
}
2012-11-26 15:57:03 -01:00
/// <summary>
/// Saves the connection string as a proper .net ConnectionString and the legacy AppSettings key/value.
/// </summary>
2012-11-26 17:58:42 -01:00
/// <remarks>
/// Saves the ConnectionString in the very nasty 'medium trust'-supportive way.
/// </remarks>
2012-11-26 15:57:03 -01:00
/// <param name="connectionString"></param>
/// <param name="providerName"></param>
2012-12-31 12:15:46 -01:00
private void SaveConnectionString ( string connectionString , string providerName )
2012-11-26 15:57:03 -01:00
{
//Set the connection string for the new datalayer
var connectionStringSettings = string . IsNullOrEmpty ( providerName )
? new ConnectionStringSettings ( GlobalSettings . UmbracoConnectionName ,
connectionString )
: new ConnectionStringSettings ( GlobalSettings . UmbracoConnectionName ,
connectionString , providerName ) ;
2012-11-27 11:38:09 -01:00
_connectionString = connectionString ;
_providerName = providerName ;
2013-10-04 10:53:22 +10:00
2013-12-06 12:16:36 +01:00
var fileName = IOHelper . MapPath ( string . Format ( "{0}/web.config" , SystemDirectories . Root ) ) ;
2012-12-11 16:23:15 -01:00
var xml = XDocument . Load ( fileName , LoadOptions . PreserveWhitespace ) ;
2013-10-04 10:53:22 +10:00
var connectionstrings = xml . Root . DescendantsAndSelf ( "connectionStrings" ) . Single ( ) ;
2012-11-26 15:57:03 -01:00
// Update connectionString if it exists, or else create a new appSetting for the given key and value
var setting = connectionstrings . Descendants ( "add" ) . FirstOrDefault ( s = > s . Attribute ( "name" ) . Value = = GlobalSettings . UmbracoConnectionName ) ;
if ( setting = = null )
2013-01-29 11:30:20 -01:00
connectionstrings . Add ( new XElement ( "add" ,
2012-11-26 15:57:03 -01:00
new XAttribute ( "name" , GlobalSettings . UmbracoConnectionName ) ,
new XAttribute ( "connectionString" , connectionStringSettings ) ,
new XAttribute ( "providerName" , providerName ) ) ) ;
else
{
setting . Attribute ( "connectionString" ) . Value = connectionString ;
2013-01-02 07:21:08 -01:00
setting . Attribute ( "providerName" ) . Value = providerName ;
2012-11-26 15:57:03 -01:00
}
2012-12-11 16:23:15 -01:00
xml . Save ( fileName , SaveOptions . DisableFormatting ) ;
2012-11-27 15:07:53 -01:00
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Configured a new ConnectionString using the '" + providerName + "' provider." ) ;
2012-11-26 15:57:03 -01:00
}
2012-10-29 09:49:31 -01:00
/// <summary>
/// Internal method to initialize the database configuration.
/// </summary>
/// <remarks>
/// If an Umbraco connectionstring exists the database can be configured on app startup,
/// but if its a new install the entry doesn't exist and the db cannot be configured.
/// So for new installs the Initialize() method should be called after the connectionstring
/// has been added to the web.config.
/// </remarks>
internal void Initialize ( )
{
2013-02-20 15:55:39 -01:00
var databaseSettings = ConfigurationManager . ConnectionStrings [ GlobalSettings . UmbracoConnectionName ] ;
if ( databaseSettings ! = null & & string . IsNullOrWhiteSpace ( databaseSettings . ConnectionString ) = = false & & string . IsNullOrWhiteSpace ( databaseSettings . ProviderName ) = = false )
2012-10-29 09:49:31 -01:00
{
var providerName = "System.Data.SqlClient" ;
2013-07-26 18:13:56 +10:00
string connString = null ;
2012-10-29 09:49:31 -01:00
if ( ! string . IsNullOrEmpty ( ConfigurationManager . ConnectionStrings [ GlobalSettings . UmbracoConnectionName ] . ProviderName ) )
2012-11-26 17:58:42 -01:00
{
providerName = ConfigurationManager . ConnectionStrings [ GlobalSettings . UmbracoConnectionName ] . ProviderName ;
2013-07-26 18:13:56 +10:00
connString = ConfigurationManager . ConnectionStrings [ GlobalSettings . UmbracoConnectionName ] . ConnectionString ;
2012-11-26 17:58:42 -01:00
}
2013-07-26 18:13:56 +10:00
Initialize ( providerName , connString ) ;
2013-08-02 16:01:54 +10:00
DetermineSqlServerVersion ( ) ;
2013-01-25 15:05:42 -01:00
}
2013-01-28 14:01:38 -01:00
else if ( ConfigurationManager . AppSettings . ContainsKey ( GlobalSettings . UmbracoConnectionName ) & & string . IsNullOrEmpty ( ConfigurationManager . AppSettings [ GlobalSettings . UmbracoConnectionName ] ) = = false )
2013-01-25 15:05:42 -01:00
{
//A valid connectionstring does not exist, but the legacy appSettings key was found, so we'll reconfigure the conn.string.
var legacyConnString = ConfigurationManager . AppSettings [ GlobalSettings . UmbracoConnectionName ] ;
if ( legacyConnString . ToLowerInvariant ( ) . Contains ( "sqlce4umbraco" ) )
{
2013-01-29 11:30:20 -01:00
ConfigureEmbeddedDatabaseConnection ( ) ;
2013-01-25 15:05:42 -01:00
}
2013-02-20 15:03:43 -01:00
else if ( legacyConnString . ToLowerInvariant ( ) . Contains ( "tcp:" ) )
2012-10-29 09:49:31 -01:00
{
2013-01-25 15:05:42 -01:00
//Must be sql azure
SaveConnectionString ( legacyConnString , "System.Data.SqlClient" ) ;
2013-01-25 15:17:05 -01:00
Initialize ( "System.Data.SqlClient" ) ;
2012-10-29 09:49:31 -01:00
}
2013-01-29 11:30:20 -01:00
else if ( legacyConnString . ToLowerInvariant ( ) . Contains ( "datalayer=mysql" ) )
2012-10-29 09:49:31 -01:00
{
2013-01-25 15:05:42 -01:00
//Must be MySql
2013-01-29 11:30:20 -01:00
//Need to strip the datalayer part off
var connectionStringWithoutDatalayer = string . Empty ;
foreach ( var variable in legacyConnString . Split ( ';' ) . Where ( x = > x . ToLowerInvariant ( ) . StartsWith ( "datalayer" ) = = false ) )
connectionStringWithoutDatalayer = string . Format ( "{0}{1};" , connectionStringWithoutDatalayer , variable ) ;
SaveConnectionString ( connectionStringWithoutDatalayer , "MySql.Data.MySqlClient" ) ;
2013-01-25 15:17:05 -01:00
Initialize ( "MySql.Data.MySqlClient" ) ;
2012-10-29 09:49:31 -01:00
}
else
{
2013-01-25 15:05:42 -01:00
//Must be sql
SaveConnectionString ( legacyConnString , "System.Data.SqlClient" ) ;
2013-01-25 15:17:05 -01:00
Initialize ( "System.Data.SqlClient" ) ;
2012-10-29 09:49:31 -01:00
}
2012-11-02 09:11:23 -01:00
2013-01-25 15:05:42 -01:00
//Remove the legacy connection string, so we don't end up in a loop if something goes wrong.
GlobalSettings . RemoveSetting ( GlobalSettings . UmbracoConnectionName ) ;
2013-08-02 16:01:54 +10:00
DetermineSqlServerVersion ( ) ;
2012-10-29 09:49:31 -01:00
}
else
{
_configured = false ;
}
}
2012-11-27 11:38:09 -01:00
internal void Initialize ( string providerName )
{
2015-01-13 18:19:52 +11:00
//only configure once!
if ( _configured = = true ) return ;
2013-03-09 10:43:34 -01:00
_providerName = providerName ;
try
2012-11-27 11:38:09 -01:00
{
2015-01-13 18:19:52 +11:00
if ( _syntaxProviders ! = null )
{
SqlSyntax = _syntaxProviders . GetByProviderNameOrDefault ( providerName ) ;
}
else if ( SqlSyntax = = null )
{
throw new InvalidOperationException ( "No " + typeof ( ISqlSyntaxProvider ) + " specified or no " + typeof ( SqlSyntaxProviders ) + " instance specified" ) ;
}
2013-03-09 10:43:34 -01:00
_configured = true ;
2012-11-27 11:38:09 -01:00
}
2013-03-09 10:43:34 -01:00
catch ( Exception e )
2012-11-27 11:38:09 -01:00
{
2013-03-09 10:43:34 -01:00
_configured = false ;
2013-01-29 11:30:20 -01:00
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Initialization of the DatabaseContext failed with following error: " + e . Message ) ;
_logger . Info < DatabaseContext > ( e . StackTrace ) ;
2013-03-09 10:43:34 -01:00
}
2012-11-27 11:38:09 -01:00
}
2012-11-29 12:40:48 -01:00
2013-07-26 18:13:56 +10:00
internal void Initialize ( string providerName , string connectionString )
{
_connectionString = connectionString ;
Initialize ( providerName ) ;
}
2013-08-02 16:01:54 +10:00
/// <summary>
/// Set the lazy resolution of determining the SQL server version if that is the db type we're using
/// </summary>
private void DetermineSqlServerVersion ( )
{
2015-01-09 13:00:26 +11:00
var sqlServerSyntax = SqlSyntax as SqlServerSyntaxProvider ;
2013-08-02 16:01:54 +10:00
if ( sqlServerSyntax ! = null )
{
//this will not execute now, it is lazy so will only execute when we need to actually know
// the sql server version.
sqlServerSyntax . VersionName = new Lazy < SqlServerVersionName > ( ( ) = >
{
try
{
var database = this . _factory . CreateDatabase ( ) ;
var version = database . ExecuteScalar < string > ( "SELECT SERVERPROPERTY('productversion')" ) ;
var firstPart = version . Split ( '.' ) [ 0 ] ;
switch ( firstPart )
{
case "11" :
return SqlServerVersionName . V2012 ;
case "10" :
return SqlServerVersionName . V2008 ;
case "9" :
return SqlServerVersionName . V2005 ;
case "8" :
return SqlServerVersionName . V2000 ;
case "7" :
return SqlServerVersionName . V7 ;
default :
return SqlServerVersionName . Other ;
}
}
catch ( Exception )
{
return SqlServerVersionName . Invalid ;
}
} ) ;
}
}
2013-01-25 15:05:42 -01:00
internal DatabaseSchemaResult ValidateDatabaseSchema ( )
{
if ( _configured = = false | | ( string . IsNullOrEmpty ( _connectionString ) | | string . IsNullOrEmpty ( ProviderName ) ) )
2015-02-22 21:36:02 +01:00
return new DatabaseSchemaResult ( SqlSyntax ) ;
2013-01-25 15:05:42 -01:00
2013-01-28 14:01:38 -01:00
if ( _result = = null )
{
2014-03-14 13:04:20 +11:00
if ( SystemUtilities . GetCurrentTrustLevel ( ) ! = AspNetHostingPermissionLevel . Unrestricted
& & ProviderName = = "MySql.Data.MySqlClient" )
{
throw new InvalidOperationException ( "Cannot use MySql in Medium Trust configuration" ) ;
}
2015-01-09 15:27:47 +11:00
var database = new UmbracoDatabase ( _connectionString , ProviderName , _logger ) ;
2015-01-09 13:00:26 +11:00
var dbSchema = new DatabaseSchemaCreation ( database , _logger , SqlSyntax ) ;
2013-01-28 14:01:38 -01:00
_result = dbSchema . ValidateSchema ( ) ;
}
return _result ;
2013-01-25 15:05:42 -01:00
}
2013-12-13 12:34:57 +11:00
internal Result CreateDatabaseSchemaAndData ( )
2014-03-14 13:04:20 +11:00
{
2012-11-29 12:40:48 -01:00
try
{
2014-03-14 13:04:20 +11:00
var readyForInstall = CheckReadyForInstall ( ) ;
if ( readyForInstall . Success = = false )
{
return readyForInstall . Result ;
}
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database configuration status: Started" ) ;
2013-02-06 09:04:46 -01:00
2013-12-13 12:34:57 +11:00
string message ;
2013-02-25 13:14:26 -01:00
2015-01-09 15:27:47 +11:00
var database = new UmbracoDatabase ( _connectionString , ProviderName , _logger ) ;
2014-04-30 19:58:59 +10:00
// If MySQL, we're going to ensure that database calls are maintaining proper casing as to remove the necessity for checks
// for case insensitive queries. In an ideal situation (which is what we're striving for), all calls would be case sensitive.
/ *
2015-01-09 13:00:26 +11:00
var supportsCaseInsensitiveQueries = SqlSyntax . SupportsCaseInsensitiveQueries ( database ) ;
2013-02-25 14:42:07 -01:00
if ( supportsCaseInsensitiveQueries = = false )
2013-02-25 13:14:26 -01:00
{
message = "<p> </p><p>The database you're trying to use does not support case insensitive queries. <br />We currently do not support these types of databases.</p>" +
2013-07-23 15:38:26 +02:00
"<p>You can fix this by changing the following setting in your my.ini file in your MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
2013-02-25 13:14:26 -01:00
"<p>Note: Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>" ;
return new Result { Message = message , Success = false , Percentage = "15" } ;
}
2014-04-30 19:58:59 +10:00
* /
2013-12-13 12:34:57 +11:00
2014-04-30 19:58:59 +10:00
message = GetResultMessageForMySql ( ) ;
2013-02-25 13:14:26 -01:00
2013-01-25 15:05:42 -01:00
var schemaResult = ValidateDatabaseSchema ( ) ;
var installedVersion = schemaResult . DetermineInstalledVersion ( ) ;
2013-02-25 13:14:26 -01:00
2013-01-25 15:05:42 -01:00
//If Configuration Status is empty and the determined version is "empty" its a new install - otherwise upgrade the existing
if ( string . IsNullOrEmpty ( GlobalSettings . ConfigurationStatus ) & & installedVersion . Equals ( new Version ( 0 , 0 , 0 ) ) )
2012-11-30 18:48:20 -01:00
{
2015-02-22 21:36:02 +01:00
var schemaHelper = new DatabaseSchemaHelper ( database , _logger , SqlSyntax ) ;
schemaHelper . CreateDatabaseSchema ( ) ;
2013-02-25 14:42:07 -01:00
message = message + "<p>Installation completed!</p>" ;
2013-12-13 12:34:57 +11:00
//now that everything is done, we need to determine the version of SQL server that is executing
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database configuration status: " + message ) ;
2013-12-13 12:34:57 +11:00
return new Result { Message = message , Success = true , Percentage = "100" } ;
2012-11-30 18:48:20 -01:00
}
2013-12-13 12:34:57 +11:00
//we need to do an upgrade so return a new status message and it will need to be done during the next step
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database requires upgrade" ) ;
2013-12-13 12:34:57 +11:00
message = "<p>Upgrading database, this may take some time...</p>" ;
return new Result
{
RequiresUpgrade = true ,
Message = message ,
Success = true ,
Percentage = "30"
} ;
}
catch ( Exception ex )
{
return HandleInstallException ( ex ) ;
}
}
/// <summary>
/// This assumes all of the previous checks are done!
/// </summary>
/// <returns></returns>
2015-02-23 00:02:12 +01:00
internal Result UpgradeSchemaAndData ( IMigrationResolver migrationResolver )
2013-12-13 12:34:57 +11:00
{
try
{
2014-03-14 13:04:20 +11:00
var readyForInstall = CheckReadyForInstall ( ) ;
if ( readyForInstall . Success = = false )
{
return readyForInstall . Result ;
}
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database upgrade started" ) ;
2013-12-13 12:34:57 +11:00
2015-01-09 15:27:47 +11:00
var database = new UmbracoDatabase ( _connectionString , ProviderName , _logger ) ;
2015-01-09 13:00:26 +11:00
//var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
2013-12-13 12:34:57 +11:00
2014-04-30 19:58:59 +10:00
var message = GetResultMessageForMySql ( ) ;
2013-12-13 12:34:57 +11:00
var schemaResult = ValidateDatabaseSchema ( ) ;
var installedVersion = schemaResult . DetermineInstalledVersion ( ) ;
//DO the upgrade!
2014-03-15 20:15:01 +11:00
var currentVersion = string . IsNullOrEmpty ( GlobalSettings . ConfigurationStatus )
2013-01-25 15:05:42 -01:00
? installedVersion
: new Version ( GlobalSettings . ConfigurationStatus ) ;
2013-12-13 12:34:57 +11:00
var targetVersion = UmbracoVersion . Current ;
2015-02-23 00:02:12 +01:00
var runner = new MigrationRunner ( migrationResolver , _logger , currentVersion , targetVersion , GlobalSettings . UmbracoMigrationName ) ;
2015-02-22 21:36:02 +01:00
var upgraded = runner . Execute ( database , DatabaseProvider , SqlSyntax , true ) ;
2013-12-13 12:34:57 +11:00
message = message + "<p>Upgrade completed!</p>" ;
2013-01-29 11:30:20 -01:00
2013-08-02 16:01:54 +10:00
//now that everything is done, we need to determine the version of SQL server that is executing
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database configuration status: " + message ) ;
2013-02-06 09:04:46 -01:00
2013-01-28 14:01:38 -01:00
return new Result { Message = message , Success = true , Percentage = "100" } ;
2012-11-29 12:40:48 -01:00
}
catch ( Exception ex )
{
2013-12-13 12:34:57 +11:00
return HandleInstallException ( ex ) ;
}
}
2014-04-30 19:58:59 +10:00
private string GetResultMessageForMySql ( )
{
2015-01-09 13:00:26 +11:00
if ( SqlSyntax . GetType ( ) = = typeof ( MySqlSyntaxProvider ) )
2014-04-30 19:58:59 +10:00
{
return "<p> </p><p>Congratulations, the database step ran successfully!</p>" +
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>" ;
}
return string . Empty ;
}
/ *
2013-12-13 12:34:57 +11:00
private string GetResultMessageForMySql ( bool? supportsCaseInsensitiveQueries )
{
if ( supportsCaseInsensitiveQueries = = null )
{
return "<p> </p><p>Warning! Could not check if your database type supports case insensitive queries. <br />We currently do not support these databases that do not support case insensitive queries.</p>" +
"<p>You can check this by looking for the following setting in your my.ini file in your MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
"<p>Note: Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>" ;
}
2015-01-09 13:00:26 +11:00
if ( SqlSyntax . GetType ( ) = = typeof ( MySqlSyntaxProvider ) )
2013-12-13 12:34:57 +11:00
{
return "<p> </p><p>Congratulations, the database step ran successfully!</p>" +
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>" ;
}
return string . Empty ;
2014-04-30 19:58:59 +10:00
} * /
2013-01-28 14:01:38 -01:00
2013-12-13 12:34:57 +11:00
private Attempt < Result > CheckReadyForInstall ( )
{
2014-03-14 13:04:20 +11:00
if ( SystemUtilities . GetCurrentTrustLevel ( ) ! = AspNetHostingPermissionLevel . Unrestricted
& & ProviderName = = "MySql.Data.MySqlClient" )
{
throw new InvalidOperationException ( "Cannot use MySql in Medium Trust configuration" ) ;
}
2013-12-13 12:34:57 +11:00
if ( _configured = = false | | ( string . IsNullOrEmpty ( _connectionString ) | | string . IsNullOrEmpty ( ProviderName ) ) )
{
return Attempt . Fail ( new Result
2013-01-28 14:01:38 -01:00
{
2013-12-13 12:34:57 +11:00
Message =
"Database configuration is invalid. Please check that the entered database exists and that the provided username and password has write access to the database." ,
Success = false ,
Percentage = "10"
} ) ;
}
return Attempt < Result > . Succeed ( ) ;
}
2013-01-28 14:01:38 -01:00
2013-12-13 12:34:57 +11:00
private Result HandleInstallException ( Exception ex )
{
2015-01-09 13:00:26 +11:00
_logger . Error < DatabaseContext > ( "Database configuration failed" , ex ) ;
2013-12-13 12:34:57 +11:00
if ( _result ! = null )
{
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "The database schema validation produced the following summary: \n" + _result . GetSummary ( ) ) ;
2012-11-29 12:40:48 -01:00
}
2013-12-13 12:34:57 +11:00
return new Result
{
Message =
"The database configuration failed with the following message: " + ex . Message +
"\n Please check log file for additional information (can be found in '/App_Data/Logs/UmbracoTraceLog.txt')" ,
Success = false ,
Percentage = "90"
} ;
2012-11-29 12:40:48 -01:00
}
internal class Result
{
2013-12-13 12:34:57 +11:00
public bool RequiresUpgrade { get ; set ; }
2012-11-29 12:40:48 -01:00
public string Message { get ; set ; }
public bool Success { get ; set ; }
public string Percentage { get ; set ; }
}
2014-03-04 19:20:36 +11:00
internal bool IsConnectionStringConfigured ( ConnectionStringSettings databaseSettings )
{
var dbIsSqlCe = false ;
if ( databaseSettings ! = null & & databaseSettings . ProviderName ! = null )
dbIsSqlCe = databaseSettings . ProviderName = = "System.Data.SqlServerCe.4.0" ;
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 ;
}
2012-10-29 09:49:31 -01:00
}
}