Merge branch 'dev-v8' of https://github.com/umbraco/Umbraco-CMS into dev-v8

This commit is contained in:
Sebastiaan Janssen
2016-11-29 18:02:49 +01:00
143 changed files with 1306 additions and 1331 deletions

View File

@@ -223,7 +223,17 @@ namespace Umbraco.Core.Components
private object GetParameter(Type componentType, Type parameterType)
{
var param = _container.TryGetInstance(parameterType);
object param;
try
{
param = _container.TryGetInstance(parameterType);
}
catch (Exception e)
{
throw new BootFailedException($"Could not get parameter of type {parameterType.FullName} for component {componentType.FullName}.", e);
}
if (param == null) throw new BootFailedException($"Could not get parameter of type {parameterType.FullName} for component {componentType.FullName}.");
return param;
}

View File

@@ -205,45 +205,6 @@ namespace Umbraco.Core.Configuration
}
}
/// <summary>
/// Gets the database connection string
/// </summary>
/// <value>The database connection string.</value>
[Obsolete("Use System.Configuration.ConfigurationManager.ConnectionStrings[\"umbracoDbDSN\"] instead")]
public static string DbDsn
{
get
{
var settings = ConfigurationManager.ConnectionStrings[UmbracoConnectionName];
var connectionString = string.Empty;
if (settings != null)
{
connectionString = settings.ConnectionString;
// The SqlCe connectionString is formatted slightly differently, so we need to update it
if (settings.ProviderName.Contains("SqlServerCe"))
connectionString = string.Format("datalayer=SQLCE4Umbraco.SqlCEHelper,SQLCE4Umbraco;{0}", connectionString);
}
return connectionString;
}
set
{
if (DbDsn != value)
{
if (value.ToLower().Contains("SQLCE4Umbraco.SqlCEHelper".ToLower()))
{
Current.DatabaseContext.ConfigureEmbeddedDatabaseConnection();
}
else
{
Current.DatabaseContext.ConfigureDatabaseConnection(value);
}
}
}
}
//TODO: Move these to constants!
public const string UmbracoConnectionName = "umbracoDbDSN";
public const string UmbracoMigrationName = "Umbraco";

View File

@@ -15,6 +15,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Plugins;
using Umbraco.Core.Services;
@@ -180,26 +181,29 @@ namespace Umbraco.Core
/// <inheritdoc/>
public virtual void Compose(ServiceContainer container)
{
// compose the very essential things that are needed to bootstrap, before anything else,
// and only these things - the rest should be composed in runtime components
// register basic things
container.RegisterSingleton<IProfiler, LogProfiler>();
container.RegisterSingleton<ProfilingLogger>();
container.RegisterSingleton<IRuntimeState, RuntimeState>();
// register caches
// need the deep clone runtime cache profiver to ensure entities are cached properly, ie
// are cloned in and cloned out - no request-based cache here since no web-based context,
// will be overriden later or
container.RegisterSingleton(_ => new CacheHelper(
// we need to have the dep clone runtime cache provider to ensure
// all entities are cached properly (cloned in and cloned out)
new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()),
new StaticCacheProvider(),
// we have no request based cache when not running in web-based context
new NullCacheProvider(),
new IsolatedRuntimeCache(type =>
// we need to have the dep clone runtime cache provider to ensure
// all entities are cached properly (cloned in and cloned out)
new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))));
container.RegisterSingleton(factory => factory.GetInstance<CacheHelper>().RuntimeCache);
new IsolatedRuntimeCache(type => new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))));
container.RegisterSingleton(f => f.GetInstance<CacheHelper>().RuntimeCache);
container.RegisterSingleton(factory => new PluginManager(factory.GetInstance<IRuntimeCacheProvider>(), factory.GetInstance<ProfilingLogger>()));
// register the plugin manager
container.RegisterSingleton(f => new PluginManager(f.GetInstance<IRuntimeCacheProvider>(), f.GetInstance<ProfilingLogger>()));
// register syntax providers
// register syntax providers - required by database factory
container.Register<ISqlSyntaxProvider, MySqlSyntaxProvider>("MySqlSyntaxProvider");
container.Register<ISqlSyntaxProvider, SqlCeSyntaxProvider>("SqlCeSyntaxProvider");
container.Register<ISqlSyntaxProvider, SqlServerSyntaxProvider>("SqlServerSyntaxProvider");
@@ -210,14 +214,21 @@ namespace Umbraco.Core
var mapperCollectionBuilder = container.RegisterCollectionBuilder<MapperCollectionBuilder>();
ComposeMapperCollection(mapperCollectionBuilder);
// register database factory
// register database factory - required to check for migrations
// will be initialized with syntax providers and a logger, and will try to configure
// from the default connection string name, if possible, else will remain non-configured
// until the database context configures it properly (eg when installing)
container.RegisterSingleton<IDatabaseFactory, DefaultDatabaseFactory>();
// register a database accessor - will be replaced
// by HybridUmbracoDatabaseAccessor in the web runtime
// register database context
container.RegisterSingleton<DatabaseContext>();
// register query factory - fixme kill
container.RegisterSingleton(f => f.GetInstance<IDatabaseFactory>().QueryFactory);
// register a database accessor - required by database factory
// will be replaced by HybridUmbracoDatabaseAccessor in the web runtime
// fixme - we should NOT be using thread static at all + will NOT get replaced = wtf?
container.RegisterSingleton<IUmbracoDatabaseAccessor, ThreadStaticUmbracoDatabaseAccessor>();
// register MainDom

View File

@@ -37,6 +37,10 @@ namespace Umbraco.Core
composition.Container.RegisterFrom<ServicesCompositionRoot>();
composition.Container.RegisterFrom<CoreModelMappersCompositionRoot>();
// register database builder
// *not* a singleton, don't want to keep it around
composition.Container.Register<DatabaseBuilder>();
//TODO: Don't think we'll need this when the resolvers are all container resolvers
composition.Container.RegisterSingleton<IServiceProvider, ActivatorServiceProvider>();
@@ -117,8 +121,7 @@ namespace Umbraco.Core
composition.Container.RegisterSingleton<IPublishedContentModelFactory, NoopPublishedContentModelFactory>();
}
internal void Initialize(
IEnumerable<ModelMapperConfiguration> modelMapperConfigurations)
internal void Initialize(IEnumerable<ModelMapperConfiguration> modelMapperConfigurations)
{
//TODO: Remove these for v8!
LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors();

View File

@@ -1,13 +1,9 @@
using System;
using LightInject;
using Umbraco.Core.Cache;
using Umbraco.Core.IO;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Plugins;
namespace Umbraco.Core.DI
{
@@ -27,9 +23,6 @@ namespace Umbraco.Core.DI
container.RegisterSingleton<IUnitOfWorkProvider, FileUnitOfWorkProvider>();
container.RegisterSingleton<IDatabaseUnitOfWorkProvider, NPocoUnitOfWorkProvider>();
// register query factory
container.RegisterSingleton<IQueryFactory, QueryFactory>();
// register repository factory
container.RegisterSingleton<RepositoryFactory>();

View File

@@ -0,0 +1,621 @@
using System;
using System.Configuration;
using System.Data.SqlServerCe;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.Migrations.Initial;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Services;
namespace Umbraco.Core
{
/// <summary>
/// Supports building and configuring the database.
/// </summary>
public class DatabaseBuilder
{
private readonly IRuntimeState _runtime;
private readonly IMigrationEntryService _migrationEntryService;
private readonly ILogger _logger;
private DatabaseSchemaResult _databaseSchemaValidationResult;
public DatabaseBuilder(DatabaseContext databaseContext, IRuntimeState runtime, IMigrationEntryService migrationEntryService, ILogger logger)
{
DatabaseContext = databaseContext;
_runtime = runtime;
_migrationEntryService = migrationEntryService;
_logger = logger;
}
public DatabaseContext DatabaseContext { get; }
#region Status
/// <summary>
/// Gets a value indicating whether the database is configured. It does not necessarily
/// mean that it is possible to connect, nor that Umbraco is installed, nor
/// up-to-date.
/// </summary>
public bool IsDatabaseConfigured => DatabaseContext.IsDatabaseConfigured;
/// <summary>
/// Gets a value indicating whether it is possible to connect to the database.
/// </summary>
public bool CanConnect => DatabaseContext.CanConnect;
// that method was originally created by Per in DatabaseHelper- tests the db connection for install
// fixed by Shannon to not-ignore the provider
// fixed by Stephan as part of the v8 persistence cleanup, now using provider names + SqlCe exception
// moved by Stephan to DatabaseBuilder
// probably needs to be cleaned up
public bool CheckConnection(string databaseType, string connectionString, string server, string database, string login, string password, bool integratedAuth)
{
// we do not test SqlCE connection
if (databaseType.InvariantContains("sqlce"))
return true;
string providerName;
if (string.IsNullOrWhiteSpace(connectionString) == false)
{
providerName = DbConnectionExtensions.DetectProviderNameFromConnectionString(connectionString);
}
else if (integratedAuth)
{
// has to be Sql Server
providerName = Constants.DbProviderNames.SqlServer;
connectionString = GetIntegratedSecurityDatabaseConnectionString(server, database);
}
else
{
connectionString = GetDatabaseConnectionString(
server, database, login, password,
databaseType, out providerName);
}
return DbConnectionExtensions.IsConnectionAvailable(connectionString, providerName);
}
#endregion
#region Configure Connection String
private 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(DatabaseContext.DatabaseFactory, _logger);
}
private static void ConfigureEmbeddedDatabaseConnection(IDatabaseFactory factory, ILogger logger)
{
SaveConnectionString(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe, logger);
var path = Path.Combine(GlobalSettings.FullpathToRoot, "App_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
var engine = new SqlCeEngine(EmbeddedDatabaseConnectionString);
engine.CreateDatabase();
}
factory.Configure(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe);
}
/// <summary>
/// Configures a connection string that has been entered manually.
/// </summary>
/// <param name="connectionString">A connection string.</param>
/// <remarks>Has to be either SQL Server or MySql</remarks>
public void ConfigureDatabaseConnection(string connectionString)
{
var provider = DbConnectionExtensions.DetectProviderNameFromConnectionString(connectionString);
var providerName = provider.ToString().ToLower().Contains("mysql")
? Constants.DbProviderNames.MySql
: Constants.DbProviderNames.SqlServer;
SaveConnectionString(connectionString, providerName, _logger);
DatabaseContext.DatabaseFactory.Configure(connectionString, providerName);
}
/// <summary>
/// Configures a connection string from the installer.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <param name="databaseProvider">The name the provider (Sql, Sql Azure, Sql Ce, MySql).</param>
public void ConfigureDatabaseConnection(string server, string databaseName, string user, string password, string databaseProvider)
{
string providerName;
var connectionString = GetDatabaseConnectionString(server, databaseName, user, password, databaseProvider, out providerName);
SaveConnectionString(connectionString, providerName, _logger);
DatabaseContext.DatabaseFactory.Configure(connectionString, providerName);
}
/// <summary>
/// Gets a connection string from the installer.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <param name="databaseProvider">The name the provider (Sql, Sql Azure, Sql Ce, MySql).</param>
/// <param name="providerName"></param>
/// <returns>A connection string.</returns>
public static string GetDatabaseConnectionString(string server, string databaseName, string user, string password, string databaseProvider, out string providerName)
{
providerName = Constants.DbProviderNames.SqlServer;
var test = databaseProvider.ToLower();
if (test.Contains("mysql"))
{
providerName = Constants.DbProviderNames.MySql;
return $"Server={server}; Database={databaseName};Uid={user};Pwd={password}";
}
if (test.Contains("azure"))
{
return GetAzureConnectionString(server, databaseName, user, password);
}
return $"server={server};database={databaseName};user id={user};password={password}";
}
/// <summary>
/// Configures a connection string using Microsoft SQL Server integrated security.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database</param>
public void ConfigureIntegratedSecurityDatabaseConnection(string server, string databaseName)
{
var connectionString = GetIntegratedSecurityDatabaseConnectionString(server, databaseName);
SaveConnectionString(connectionString, Constants.DbProviderNames.SqlServer, _logger);
DatabaseContext.DatabaseFactory.Configure(connectionString, Constants.DbProviderNames.SqlServer);
}
/// <summary>
/// Gets a connection string using Microsoft SQL Server integrated security.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database</param>
/// <returns>A connection string.</returns>
public static string GetIntegratedSecurityDatabaseConnectionString(string server, string databaseName)
{
return $"Server={server};Database={databaseName};Integrated Security=true";
}
/// <summary>
/// Gets an Azure connection string.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <returns>A connection string.</returns>
public static string GetAzureConnectionString(string server, string databaseName, string user, string password)
{
if (server.Contains(".") && ServerStartsWithTcp(server) == false)
server = $"tcp:{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 = $"{serverName}.database.windows.net{portAddition}";
}
if (ServerStartsWithTcp(server) == false)
server = $"tcp:{server}.database.windows.net";
if (server.Contains(",") == false)
server = $"{server},1433";
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 = $"{user}@{userDomain}";
}
return $"Server={server};Database={databaseName};User ID={user};Password={password}";
}
private static bool ServerStartsWithTcp(string server)
{
return server.ToLower().StartsWith("tcp:".ToLower());
}
/// <summary>
/// Saves the connection string as a proper .net connection string in web.config.
/// </summary>
/// <remarks>Saves the ConnectionString in the very nasty 'medium trust'-supportive way.</remarks>
/// <param name="connectionString">The connection string.</param>
/// <param name="providerName">The provider name.</param>
/// <param name="logger">A logger.</param>
private static void SaveConnectionString(string connectionString, string providerName, ILogger logger)
{
if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullOrEmptyException(nameof(connectionString));
if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentNullOrEmptyException(nameof(providerName));
// set the connection string for the new datalayer
var connectionStringSettings = new ConnectionStringSettings(GlobalSettings.UmbracoConnectionName, connectionString, providerName);
var fileName = IOHelper.MapPath($"{SystemDirectories.Root}/web.config");
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
if (xml.Root == null) throw new Exception("Invalid web.config file.");
var connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault();
if (connectionStrings == null) throw new Exception("Invalid web.config file.");
// update connectionString if it exists, or else create a new connectionString
var setting = connectionStrings.Descendants("add").FirstOrDefault(s => s.Attribute("name").Value == GlobalSettings.UmbracoConnectionName);
if (setting == null)
{
connectionStrings.Add(new XElement("add",
new XAttribute("name", GlobalSettings.UmbracoConnectionName),
new XAttribute("connectionString", connectionStringSettings),
new XAttribute("providerName", providerName)));
}
else
{
setting.Attribute("connectionString").Value = connectionString;
setting.Attribute("providerName").Value = providerName;
}
xml.Save(fileName, SaveOptions.DisableFormatting);
logger.Info<DatabaseContext>("Configured a new ConnectionString using the '" + providerName + "' provider.");
}
internal bool IsConnectionStringConfigured(ConnectionStringSettings 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;
}
#endregion
#region Utils
internal static void GiveLegacyAChance(IDatabaseFactory factory, ILogger logger)
{
// look for the legacy appSettings key
var legacyConnString = ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName];
if (string.IsNullOrWhiteSpace(legacyConnString)) return;
var test = legacyConnString.ToLowerInvariant();
if (test.Contains("sqlce4umbraco"))
{
// sql ce
ConfigureEmbeddedDatabaseConnection(factory, logger);
}
else if (test.Contains("tcp:"))
{
// sql azure
SaveConnectionString(legacyConnString, Constants.DbProviderNames.SqlServer, logger);
factory.Configure(legacyConnString, Constants.DbProviderNames.SqlServer);
}
else if (test.Contains("datalayer=mysql"))
{
// mysql
// strip the datalayer part off
var connectionStringWithoutDatalayer = string.Empty;
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var variable in legacyConnString.Split(';').Where(x => x.ToLowerInvariant().StartsWith("datalayer") == false))
connectionStringWithoutDatalayer = $"{connectionStringWithoutDatalayer}{variable};";
SaveConnectionString(connectionStringWithoutDatalayer, Constants.DbProviderNames.MySql, logger);
factory.Configure(connectionStringWithoutDatalayer, Constants.DbProviderNames.MySql);
}
else
{
// sql server
SaveConnectionString(legacyConnString, Constants.DbProviderNames.SqlServer, logger);
factory.Configure(legacyConnString, Constants.DbProviderNames.SqlServer);
}
// remove the legacy connection string, so we don't end up in a loop if something goes wrong
GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName);
}
#endregion
#region Database Schema
internal DatabaseSchemaResult ValidateDatabaseSchema()
{
if (DatabaseContext.DatabaseFactory.Configured == false)
return new DatabaseSchemaResult(DatabaseContext.SqlSyntax);
if (_databaseSchemaValidationResult != null)
return _databaseSchemaValidationResult;
var database = DatabaseContext.DatabaseFactory.GetDatabase();
var dbSchema = new DatabaseSchemaCreation(database, _logger);
_databaseSchemaValidationResult = dbSchema.ValidateSchema();
return _databaseSchemaValidationResult;
}
internal Result CreateDatabaseSchemaAndData()
{
try
{
var readyForInstall = CheckReadyForInstall();
if (readyForInstall.Success == false)
{
return readyForInstall.Result;
}
_logger.Info<DatabaseContext>("Database configuration status: Started");
var database = DatabaseContext.DatabaseFactory.GetDatabase();
// 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.
/*
var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
if (supportsCaseInsensitiveQueries == false)
{
message = "<p>&nbsp;</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>" +
"<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 />" +
"<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" };
}
*/
var message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
var installedSchemaVersion = schemaResult.DetermineInstalledVersion();
//If Configuration Status is empty and the determined version is "empty" its a new install - otherwise upgrade the existing
if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) && installedSchemaVersion.Equals(new Version(0, 0, 0)))
{
var helper = new DatabaseSchemaHelper(database, _logger);
helper.CreateDatabaseSchema(_runtime, _migrationEntryService, true);
message = message + "<p>Installation completed!</p>";
//now that everything is done, we need to determine the version of SQL server that is executing
_logger.Info<DatabaseContext>("Database configuration status: " + message);
return new Result { Message = message, Success = true, Percentage = "100" };
}
//we need to do an upgrade so return a new status message and it will need to be done during the next step
_logger.Info<DatabaseContext>("Database requires upgrade");
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>
internal Result UpgradeSchemaAndData(IMigrationEntryService migrationEntryService, MigrationCollectionBuilder builder)
{
try
{
var readyForInstall = CheckReadyForInstall();
if (readyForInstall.Success == false)
{
return readyForInstall.Result;
}
_logger.Info<DatabaseContext>("Database upgrade started");
var database = DatabaseContext.DatabaseFactory.GetDatabase();
//var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
var message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
var installedSchemaVersion = new SemVersion(schemaResult.DetermineInstalledVersion());
var installedMigrationVersion = schemaResult.DetermineInstalledVersionByMigrations(migrationEntryService);
var targetVersion = UmbracoVersion.Current;
//In some cases - like upgrading from 7.2.6 -> 7.3, there will be no migration information in the database and therefore it will
// return a version of 0.0.0 and we don't necessarily want to run all migrations from 0 -> 7.3, so we'll just ensure that the
// migrations are run for the target version
if (installedMigrationVersion == new SemVersion(new Version(0, 0, 0)) && installedSchemaVersion > new SemVersion(new Version(0, 0, 0)))
{
//set the installedMigrationVersion to be one less than the target so the latest migrations are guaranteed to execute
installedMigrationVersion = new SemVersion(targetVersion.SubtractRevision());
}
//Figure out what our current installed version is. If the web.config doesn't have a version listed, then we'll use the minimum
// version detected between the schema installed and the migrations listed in the migration table.
// If there is a version in the web.config, we'll take the minimum between the listed migration in the db and what
// is declared in the web.config.
var currentInstalledVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
//Take the minimum version between the detected schema version and the installed migration version
? new[] { installedSchemaVersion, installedMigrationVersion }.Min()
//Take the minimum version between the installed migration version and the version specified in the config
: new[] { SemVersion.Parse(GlobalSettings.ConfigurationStatus), installedMigrationVersion }.Min();
//Ok, another edge case here. If the current version is a pre-release,
// then we want to ensure all migrations for the current release are executed.
if (currentInstalledVersion.Prerelease.IsNullOrWhiteSpace() == false)
{
currentInstalledVersion = new SemVersion(currentInstalledVersion.GetVersion().SubtractRevision());
}
//DO the upgrade!
var runner = new MigrationRunner(builder, migrationEntryService, _logger, currentInstalledVersion, UmbracoVersion.SemanticVersion, GlobalSettings.UmbracoMigrationName);
var migrationContext = new MigrationContext(database, _logger);
var upgraded = runner.Execute(migrationContext /*, true*/);
if (upgraded == false)
{
throw new ApplicationException("Upgrading failed, either an error occurred during the upgrade process or an event canceled the upgrade process, see log for full details");
}
message = message + "<p>Upgrade completed!</p>";
//now that everything is done, we need to determine the version of SQL server that is executing
_logger.Info<DatabaseContext>("Database configuration status: " + message);
return new Result { Message = message, Success = true, Percentage = "100" };
}
catch (Exception ex)
{
return HandleInstallException(ex);
}
}
private string GetResultMessageForMySql()
{
if (DatabaseContext.SqlSyntax.GetType() == typeof(MySqlSyntaxProvider))
{
return "<p>&nbsp;</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;
}
/*
private string GetResultMessageForMySql(bool? supportsCaseInsensitiveQueries)
{
if (supportsCaseInsensitiveQueries == null)
{
return "<p>&nbsp;</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>";
}
if (SqlSyntax.GetType() == typeof(MySqlSyntaxProvider))
{
return "<p>&nbsp;</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;
}*/
private Attempt<Result> CheckReadyForInstall()
{
if (DatabaseContext.DatabaseFactory.Configured == false)
{
return Attempt.Fail(new Result
{
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();
}
private Result HandleInstallException(Exception ex)
{
_logger.Error<DatabaseContext>("Database configuration failed", ex);
if (_databaseSchemaValidationResult != null)
{
_logger.Info<DatabaseContext>("The database schema validation produced the following summary: \n" + _databaseSchemaValidationResult.GetSummary());
}
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"
};
}
internal class Result
{
public bool RequiresUpgrade { get; set; }
public string Message { get; set; }
public bool Success { get; set; }
public string Percentage { get; set; }
}
#endregion
}
}

View File

@@ -1,24 +1,10 @@
using System;
using System.Configuration;
using System.Data.SqlServerCe;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Configuration;
using System.Xml.Linq;
using NPoco;
using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.DI;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.Migrations.Initial;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Services;
namespace Umbraco.Core
{
@@ -28,43 +14,64 @@ namespace Umbraco.Core
/// <remarks>One per AppDomain. Ensures that the database is available.</remarks>
public class DatabaseContext
{
private readonly IDatabaseFactory _factory;
private readonly ILogger _logger;
private readonly IRuntimeState _runtime;
private readonly IMigrationEntryService _migrationEntryService;
private DatabaseSchemaResult _databaseSchemaValidationResult;
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseContext"/> class.
/// </summary>
/// <param name="factory">A database factory.</param>
/// <param name="logger">A logger.</param>
/// <param name="runtime"></param>
/// <param name="migrationEntryService"></param>
/// <param name="databaseFactory">A database factory.</param>
/// <remarks>The database factory will try to configure itself but may fail eg if the default
/// Umbraco connection string is not available because we are installing. In which case this
/// database context must sort things out and configure the database factory before it can be
/// used.</remarks>
public DatabaseContext(IDatabaseFactory factory, ILogger logger, IRuntimeState runtime, IMigrationEntryService migrationEntryService)
public DatabaseContext(IDatabaseFactory databaseFactory)
{
if (factory == null) throw new ArgumentNullException(nameof(factory));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (databaseFactory == null) throw new ArgumentNullException(nameof(databaseFactory));
_factory = factory;
_logger = logger;
_runtime = runtime;
_migrationEntryService = migrationEntryService;
DatabaseFactory = databaseFactory;
}
// FIXME
// this is basically exposing a subset of the database factory...
// so why? why not "just" merge with database factory?
// which can create & manage ambient database,
// create Sql<SqlContext> expressions
// create IQuery<T> expressions
// ?
internal IDatabaseFactory DatabaseFactory { get; }
/// <summary>
/// Gets the QueryFactory
/// </summary>
public IQueryFactory QueryFactory => _factory.QueryFactory;
public IQueryFactory QueryFactory => DatabaseFactory.QueryFactory; // fixme obsolete?
/// <summary>
/// Gets the database sql syntax.
/// </summary>
public ISqlSyntaxProvider SqlSyntax => _factory.QueryFactory.SqlSyntax;
public ISqlSyntaxProvider SqlSyntax => DatabaseFactory.SqlSyntax;
// fixme
// created by the database factory?
// add PocoDataFactory
// add DatabaseType
// add Sql() and Query<T>()
// so it can finally replace SqlContext entirely?
// need an IDatabaseContext interface?
public Sql<SqlContext> Sql()
{
var factory = (DefaultDatabaseFactory) DatabaseFactory; // fixme
return NPoco.Sql.BuilderFor(factory.SqlContext);
}
public Sql<SqlContext> Sql(string sql, params object[] args)
{
return Sql().Append(sql, args);
}
public IQuery<T> Query<T>()
{
return DatabaseFactory.QueryFactory.Create<T>();
}
/// <summary>
/// Gets the <see cref="Database"/> object for doing CRUD operations
@@ -74,14 +81,14 @@ namespace Umbraco.Core
/// This should not be used for CRUD operations or queries against the
/// standard Umbraco tables! Use the Public services for that.
/// </remarks>
public UmbracoDatabase Database => _factory.GetDatabase();
public UmbracoDatabase Database => DatabaseFactory.GetDatabase();
/// <summary>
/// Gets a value indicating whether the database is configured. It does not necessarily
/// mean that it is possible to connect, nor that Umbraco is installed, nor
/// up-to-date.
/// </summary>
public bool IsDatabaseConfigured => _factory.Configured;
public bool IsDatabaseConfigured => DatabaseFactory.Configured;
/// <summary>
/// Gets a value indicating whether it is possible to connect to the database.
@@ -90,547 +97,11 @@ namespace Umbraco.Core
{
get
{
if (IsDatabaseConfigured == false) return false;
var canConnect = _factory.CanConnect;
if (DatabaseFactory.Configured == false) return false;
var canConnect = DatabaseFactory.CanConnect;
Current.Logger.Info<DatabaseContext>("CanConnect = " + canConnect);
return canConnect;
}
}
#region Configure Connection String
private 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(_factory, _logger);
}
private static void ConfigureEmbeddedDatabaseConnection(IDatabaseFactory factory, ILogger logger)
{
SaveConnectionString(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe, logger);
var path = Path.Combine(GlobalSettings.FullpathToRoot, "App_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
var engine = new SqlCeEngine(EmbeddedDatabaseConnectionString);
engine.CreateDatabase();
}
factory.Configure(EmbeddedDatabaseConnectionString, Constants.DbProviderNames.SqlCe);
}
/// <summary>
/// Configures a connection string that has been entered manually.
/// </summary>
/// <param name="connectionString">A connection string.</param>
/// <remarks>Has to be either SQL Server or MySql</remarks>
public void ConfigureDatabaseConnection(string connectionString)
{
var provider = DbConnectionExtensions.DetectProviderNameFromConnectionString(connectionString);
var providerName = provider.ToString().ToLower().Contains("mysql")
? Constants.DbProviderNames.MySql
: Constants.DbProviderNames.SqlServer;
SaveConnectionString(connectionString, providerName, _logger);
_factory.Configure(connectionString, providerName);
}
/// <summary>
/// Configures a connection string from the installer.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <param name="databaseProvider">The name the provider (Sql, Sql Azure, Sql Ce, MySql).</param>
public void ConfigureDatabaseConnection(string server, string databaseName, string user, string password, string databaseProvider)
{
string providerName;
var connectionString = GetDatabaseConnectionString(server, databaseName, user, password, databaseProvider, out providerName);
SaveConnectionString(connectionString, providerName, _logger);
_factory.Configure(connectionString, providerName);
}
/// <summary>
/// Gets a connection string from the installer.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <param name="databaseProvider">The name the provider (Sql, Sql Azure, Sql Ce, MySql).</param>
/// <param name="providerName"></param>
/// <returns>A connection string.</returns>
public static string GetDatabaseConnectionString(string server, string databaseName, string user, string password, string databaseProvider, out string providerName)
{
providerName = Constants.DbProviderNames.SqlServer;
var test = databaseProvider.ToLower();
if (test.Contains("mysql"))
{
providerName = Constants.DbProviderNames.MySql;
return $"Server={server}; Database={databaseName};Uid={user};Pwd={password}";
}
if (test.Contains("azure"))
{
return GetAzureConnectionString(server, databaseName, user, password);
}
return $"server={server};database={databaseName};user id={user};password={password}";
}
/// <summary>
/// Configures a connection string using Microsoft SQL Server integrated security.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database</param>
public void ConfigureIntegratedSecurityDatabaseConnection(string server, string databaseName)
{
var connectionString = GetIntegratedSecurityDatabaseConnectionString(server, databaseName);
SaveConnectionString(connectionString, Constants.DbProviderNames.SqlServer, _logger);
_factory.Configure(connectionString, Constants.DbProviderNames.SqlServer);
}
/// <summary>
/// Gets a connection string using Microsoft SQL Server integrated security.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database</param>
/// <returns>A connection string.</returns>
public static string GetIntegratedSecurityDatabaseConnectionString(string server, string databaseName)
{
return $"Server={server};Database={databaseName};Integrated Security=true";
}
/// <summary>
/// Gets an Azure connection string.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <returns>A connection string.</returns>
public static string GetAzureConnectionString(string server, string databaseName, string user, string password)
{
if (server.Contains(".") && ServerStartsWithTcp(server) == false)
server = $"tcp:{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 = $"{serverName}.database.windows.net{portAddition}";
}
if (ServerStartsWithTcp(server) == false)
server = $"tcp:{server}.database.windows.net";
if (server.Contains(",") == false)
server = $"{server},1433";
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 = $"{user}@{userDomain}";
}
return $"Server={server};Database={databaseName};User ID={user};Password={password}";
}
private static bool ServerStartsWithTcp(string server)
{
return server.ToLower().StartsWith("tcp:".ToLower());
}
/// <summary>
/// Saves the connection string as a proper .net connection string in web.config.
/// </summary>
/// <remarks>Saves the ConnectionString in the very nasty 'medium trust'-supportive way.</remarks>
/// <param name="connectionString">The connection string.</param>
/// <param name="providerName">The provider name.</param>
/// <param name="logger">A logger.</param>
private static void SaveConnectionString(string connectionString, string providerName, ILogger logger)
{
if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentNullOrEmptyException(nameof(connectionString));
if (string.IsNullOrWhiteSpace(providerName)) throw new ArgumentNullOrEmptyException(nameof(providerName));
// set the connection string for the new datalayer
var connectionStringSettings = new ConnectionStringSettings(GlobalSettings.UmbracoConnectionName, connectionString, providerName);
var fileName = IOHelper.MapPath($"{SystemDirectories.Root}/web.config");
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
if (xml.Root == null) throw new Exception("Invalid web.config file.");
var connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault();
if (connectionStrings == null) throw new Exception("Invalid web.config file.");
// update connectionString if it exists, or else create a new connectionString
var setting = connectionStrings.Descendants("add").FirstOrDefault(s => s.Attribute("name").Value == GlobalSettings.UmbracoConnectionName);
if (setting == null)
{
connectionStrings.Add(new XElement("add",
new XAttribute("name", GlobalSettings.UmbracoConnectionName),
new XAttribute("connectionString", connectionStringSettings),
new XAttribute("providerName", providerName)));
}
else
{
setting.Attribute("connectionString").Value = connectionString;
setting.Attribute("providerName").Value = providerName;
}
xml.Save(fileName, SaveOptions.DisableFormatting);
logger.Info<DatabaseContext>("Configured a new ConnectionString using the '" + providerName + "' provider.");
}
#endregion
#region Utils
internal static void GiveLegacyAChance(IDatabaseFactory factory, ILogger logger)
{
// look for the legacy appSettings key
var legacyConnString = ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName];
if (string.IsNullOrWhiteSpace(legacyConnString)) return;
var test = legacyConnString.ToLowerInvariant();
if (test.Contains("sqlce4umbraco"))
{
// sql ce
ConfigureEmbeddedDatabaseConnection(factory, logger);
}
else if (test.Contains("tcp:"))
{
// sql azure
SaveConnectionString(legacyConnString, Constants.DbProviderNames.SqlServer, logger);
factory.Configure(legacyConnString, Constants.DbProviderNames.SqlServer);
}
else if (test.Contains("datalayer=mysql"))
{
// mysql
// strip the datalayer part off
var connectionStringWithoutDatalayer = string.Empty;
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var variable in legacyConnString.Split(';').Where(x => x.ToLowerInvariant().StartsWith("datalayer") == false))
connectionStringWithoutDatalayer = $"{connectionStringWithoutDatalayer}{variable};";
SaveConnectionString(connectionStringWithoutDatalayer, Constants.DbProviderNames.MySql, logger);
factory.Configure(connectionStringWithoutDatalayer, Constants.DbProviderNames.MySql);
}
else
{
// sql server
SaveConnectionString(legacyConnString, Constants.DbProviderNames.SqlServer, logger);
factory.Configure(legacyConnString, Constants.DbProviderNames.SqlServer);
}
// remove the legacy connection string, so we don't end up in a loop if something goes wrong
GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName);
}
#endregion
#region Database Schema
internal DatabaseSchemaResult ValidateDatabaseSchema()
{
if (_factory.Configured == false)
return new DatabaseSchemaResult(SqlSyntax);
if (_databaseSchemaValidationResult != null)
return _databaseSchemaValidationResult;
var database = _factory.GetDatabase();
var dbSchema = new DatabaseSchemaCreation(database, _logger);
_databaseSchemaValidationResult = dbSchema.ValidateSchema();
return _databaseSchemaValidationResult;
}
internal Result CreateDatabaseSchemaAndData()
{
try
{
var readyForInstall = CheckReadyForInstall();
if (readyForInstall.Success == false)
{
return readyForInstall.Result;
}
_logger.Info<DatabaseContext>("Database configuration status: Started");
var database = _factory.GetDatabase();
// 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.
/*
var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
if (supportsCaseInsensitiveQueries == false)
{
message = "<p>&nbsp;</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>" +
"<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 />" +
"<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" };
}
*/
var message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
var installedSchemaVersion = schemaResult.DetermineInstalledVersion();
//If Configuration Status is empty and the determined version is "empty" its a new install - otherwise upgrade the existing
if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) && installedSchemaVersion.Equals(new Version(0, 0, 0)))
{
var helper = new DatabaseSchemaHelper(database, _logger);
helper.CreateDatabaseSchema(_runtime, _migrationEntryService, true);
message = message + "<p>Installation completed!</p>";
//now that everything is done, we need to determine the version of SQL server that is executing
_logger.Info<DatabaseContext>("Database configuration status: " + message);
return new Result { Message = message, Success = true, Percentage = "100" };
}
//we need to do an upgrade so return a new status message and it will need to be done during the next step
_logger.Info<DatabaseContext>("Database requires upgrade");
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>
internal Result UpgradeSchemaAndData(IMigrationEntryService migrationEntryService, MigrationCollectionBuilder builder)
{
try
{
var readyForInstall = CheckReadyForInstall();
if (readyForInstall.Success == false)
{
return readyForInstall.Result;
}
_logger.Info<DatabaseContext>("Database upgrade started");
var database = _factory.GetDatabase();
//var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
var message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
var installedSchemaVersion = new SemVersion(schemaResult.DetermineInstalledVersion());
var installedMigrationVersion = schemaResult.DetermineInstalledVersionByMigrations(migrationEntryService);
var targetVersion = UmbracoVersion.Current;
//In some cases - like upgrading from 7.2.6 -> 7.3, there will be no migration information in the database and therefore it will
// return a version of 0.0.0 and we don't necessarily want to run all migrations from 0 -> 7.3, so we'll just ensure that the
// migrations are run for the target version
if (installedMigrationVersion == new SemVersion(new Version(0, 0, 0)) && installedSchemaVersion > new SemVersion(new Version(0, 0, 0)))
{
//set the installedMigrationVersion to be one less than the target so the latest migrations are guaranteed to execute
installedMigrationVersion = new SemVersion(targetVersion.SubtractRevision());
}
//Figure out what our current installed version is. If the web.config doesn't have a version listed, then we'll use the minimum
// version detected between the schema installed and the migrations listed in the migration table.
// If there is a version in the web.config, we'll take the minimum between the listed migration in the db and what
// is declared in the web.config.
var currentInstalledVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
//Take the minimum version between the detected schema version and the installed migration version
? new[] {installedSchemaVersion, installedMigrationVersion}.Min()
//Take the minimum version between the installed migration version and the version specified in the config
: new[] { SemVersion.Parse(GlobalSettings.ConfigurationStatus), installedMigrationVersion }.Min();
//Ok, another edge case here. If the current version is a pre-release,
// then we want to ensure all migrations for the current release are executed.
if (currentInstalledVersion.Prerelease.IsNullOrWhiteSpace() == false)
{
currentInstalledVersion = new SemVersion(currentInstalledVersion.GetVersion().SubtractRevision());
}
//DO the upgrade!
var runner = new MigrationRunner(builder, migrationEntryService, _logger, currentInstalledVersion, UmbracoVersion.SemanticVersion, GlobalSettings.UmbracoMigrationName);
var migrationContext = new MigrationContext(database, _logger);
var upgraded = runner.Execute(migrationContext /*, true*/);
if (upgraded == false)
{
throw new ApplicationException("Upgrading failed, either an error occurred during the upgrade process or an event canceled the upgrade process, see log for full details");
}
message = message + "<p>Upgrade completed!</p>";
//now that everything is done, we need to determine the version of SQL server that is executing
_logger.Info<DatabaseContext>("Database configuration status: " + message);
return new Result { Message = message, Success = true, Percentage = "100" };
}
catch (Exception ex)
{
return HandleInstallException(ex);
}
}
private string GetResultMessageForMySql()
{
if (SqlSyntax.GetType() == typeof(MySqlSyntaxProvider))
{
return "<p>&nbsp;</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;
}
/*
private string GetResultMessageForMySql(bool? supportsCaseInsensitiveQueries)
{
if (supportsCaseInsensitiveQueries == null)
{
return "<p>&nbsp;</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>";
}
if (SqlSyntax.GetType() == typeof(MySqlSyntaxProvider))
{
return "<p>&nbsp;</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;
}*/
private Attempt<Result> CheckReadyForInstall()
{
if (_factory.Configured == false)
{
return Attempt.Fail(new Result
{
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();
}
private Result HandleInstallException(Exception ex)
{
_logger.Error<DatabaseContext>("Database configuration failed", ex);
if (_databaseSchemaValidationResult != null)
{
_logger.Info<DatabaseContext>("The database schema validation produced the following summary: \n" + _databaseSchemaValidationResult.GetSummary());
}
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"
};
}
internal class Result
{
public bool RequiresUpgrade { get; set; }
public string Message { get; set; }
public bool Success { get; set; }
public string Percentage { get; set; }
}
#endregion
internal bool IsConnectionStringConfigured(ConnectionStringSettings 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;
}
// fixme - kill!
public Sql<SqlContext> Sql()
{
return Database.Sql();
}
}
}

View File

@@ -13,7 +13,8 @@
/// itself it the factory has no model for that content type.</returns>
IPublishedContent CreateModel(IPublishedContent content);
T CreateModel<T>(IPublishedFragment content);
// temp - dont break MB
//T CreateModel<T>(IPublishedFragment content);
// fixme
// and we'd need a

View File

@@ -1,8 +1,7 @@
using NPoco;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Web.PublishedCache.NuCache.DataSource
namespace Umbraco.Core.Models.Rdbms
{
[TableName("cmsContentNu")]
[PrimaryKey("nodeId", AutoIncrement = false)]

View File

@@ -30,6 +30,7 @@ namespace Umbraco.Core.Persistence
{
private readonly IUmbracoDatabaseAccessor _umbracoDatabaseAccessor;
private readonly ISqlSyntaxProvider[] _sqlSyntaxProviders;
private readonly IMapperCollection _mappers;
private readonly ILogger _logger;
private DatabaseFactory _databaseFactory;
@@ -39,6 +40,8 @@ namespace Umbraco.Core.Persistence
private DbProviderFactory _dbProviderFactory;
private DatabaseType _databaseType;
private ISqlSyntaxProvider _sqlSyntax;
private IQueryFactory _queryFactory;
private SqlContext _sqlContext;
private RetryPolicy _connectionRetryPolicy;
private RetryPolicy _commandRetryPolicy;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
@@ -49,13 +52,13 @@ namespace Umbraco.Core.Persistence
/// <param name="sqlSyntaxProviders">The collection of available sql syntax providers.</param>
/// <param name="logger">A logger.</param>
/// <param name="umbracoDatabaseAccessor"></param>
/// <param name="queryFactory"></param>
/// <param name="mappers"></param>
/// <remarks>Used by LightInject.</remarks>
public DefaultDatabaseFactory(IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IQueryFactory queryFactory)
: this(GlobalSettings.UmbracoConnectionName, sqlSyntaxProviders, logger, umbracoDatabaseAccessor, queryFactory)
public DefaultDatabaseFactory(IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IMapperCollection mappers)
: this(GlobalSettings.UmbracoConnectionName, sqlSyntaxProviders, logger, umbracoDatabaseAccessor, mappers)
{
if (Configured == false)
DatabaseContext.GiveLegacyAChance(this, logger);
DatabaseBuilder.GiveLegacyAChance(this, logger);
}
/// <summary>
@@ -65,17 +68,17 @@ namespace Umbraco.Core.Persistence
/// <param name="sqlSyntaxProviders">The collection of available sql syntax providers.</param>
/// <param name="logger">A logger</param>
/// <param name="umbracoDatabaseAccessor"></param>
/// <param name="queryFactory"></param>
/// <param name="mappers"></param>
/// <remarks>Used by the other ctor and in tests.</remarks>
public DefaultDatabaseFactory(string connectionStringName, IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IQueryFactory queryFactory)
public DefaultDatabaseFactory(string connectionStringName, IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IMapperCollection mappers)
{
if (sqlSyntaxProviders == null) throw new ArgumentNullException(nameof(sqlSyntaxProviders));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (umbracoDatabaseAccessor == null) throw new ArgumentNullException(nameof(umbracoDatabaseAccessor));
if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentNullOrEmptyException(nameof(connectionStringName));
if (queryFactory == null) throw new ArgumentNullException(nameof(queryFactory));
if (mappers == null) throw new ArgumentNullException(nameof(mappers));
QueryFactory = queryFactory;
_mappers = mappers;
_sqlSyntaxProviders = sqlSyntaxProviders.ToArray();
_logger = logger;
_umbracoDatabaseAccessor = umbracoDatabaseAccessor;
@@ -105,16 +108,16 @@ namespace Umbraco.Core.Persistence
/// <param name="sqlSyntaxProviders">The collection of available sql syntax providers.</param>
/// <param name="logger">A logger.</param>
/// <param name="umbracoDatabaseAccessor"></param>
/// <param name="queryFactory"></param>
/// <param name="mappers"></param>
/// <remarks>Used in tests.</remarks>
public DefaultDatabaseFactory(string connectionString, string providerName, IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IQueryFactory queryFactory)
public DefaultDatabaseFactory(string connectionString, string providerName, IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IUmbracoDatabaseAccessor umbracoDatabaseAccessor, IMapperCollection mappers)
{
if (sqlSyntaxProviders == null) throw new ArgumentNullException(nameof(sqlSyntaxProviders));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (umbracoDatabaseAccessor == null) throw new ArgumentNullException(nameof(umbracoDatabaseAccessor));
if (queryFactory == null) throw new ArgumentNullException(nameof(queryFactory));
if (mappers == null) throw new ArgumentNullException(nameof(mappers));
QueryFactory = queryFactory;
_mappers = mappers;
_sqlSyntaxProviders = sqlSyntaxProviders.ToArray();
_logger = logger;
_umbracoDatabaseAccessor = umbracoDatabaseAccessor;
@@ -154,7 +157,16 @@ namespace Umbraco.Core.Persistence
/// <summary>
/// Gets the database query factory.
/// </summary>
public IQueryFactory QueryFactory { get; }
public IQueryFactory QueryFactory {
get
{
EnsureConfigured();
return _queryFactory;
}
}
// fixme
public SqlContext SqlContext => _sqlContext;
// will be configured by the database context
public void Configure(string connectionString, string providerName)
@@ -199,6 +211,12 @@ namespace Umbraco.Core.Persistence
if (_databaseFactory == null) throw new NullReferenceException("The call to DatabaseFactory.Config yielded a null DatabaseFactory instance.");
// these are created here because it is the DefaultDatabaseFactory that determines
// the sql syntax, poco data factory, and database type - so it "owns" the context
// and the query factory
_sqlContext = new SqlContext(_sqlSyntax, _pocoDataFactory, _databaseType);
_queryFactory = new QueryFactory(_sqlSyntax, _mappers);
_logger.Debug<DefaultDatabaseFactory>("Configured.");
Configured = true;
}
@@ -232,7 +250,7 @@ namespace Umbraco.Core.Persistence
// method used by NPoco's DatabaseFactory to actually create the database instance
private UmbracoDatabase CreateDatabaseInstance()
{
return new UmbracoDatabase(_connectionString, _sqlSyntax, _databaseType, _dbProviderFactory, _logger, _connectionRetryPolicy, _commandRetryPolicy);
return new UmbracoDatabase(_connectionString, _sqlContext, _dbProviderFactory, _logger, _connectionRetryPolicy, _commandRetryPolicy);
}
/// <summary>
@@ -246,7 +264,7 @@ namespace Umbraco.Core.Persistence
// check if it's in scope
var db = _umbracoDatabaseAccessor.UmbracoDatabase;
if (db != null) return db;
db = (UmbracoDatabase)_databaseFactory.GetDatabase();
db = (UmbracoDatabase) _databaseFactory.GetDatabase();
_umbracoDatabaseAccessor.UmbracoDatabase = db;
return db;
}

View File

@@ -22,5 +22,7 @@ namespace Umbraco.Core.Persistence
bool CanConnect { get; }
IQueryFactory QueryFactory { get; }
ISqlSyntaxProvider SqlSyntax { get; }
}
}

View File

@@ -84,7 +84,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{46, typeof (UmbracoDeployChecksumDto)},
{47, typeof (UmbracoDeployDependencyDto)},
{48, typeof (RedirectUrlDto) },
{49, typeof (LockDto) }
{49, typeof (LockDto) },
{50, typeof (ContentNuDto) }
};
#endregion

View File

@@ -1,5 +1,4 @@
using System;
using NPoco;
using NPoco;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter;
using Umbraco.Core.Persistence.Migrations.Syntax.Create;
@@ -45,14 +44,11 @@ namespace Umbraco.Core.Persistence.Migrations
public IUpdateBuilder Update => new UpdateBuilder(Context);
protected Sql<SqlContext> Sql() => Context.Database.Sql();
public IIfDatabaseBuilder IfDatabase(params DatabaseType[] supportedDatabaseTypes)
{
return new IfDatabaseBuilder(Context, supportedDatabaseTypes);
}
protected Sql<SqlContext> Sql()
{
return Context.Database.Sql();
}
}
}

View File

@@ -30,26 +30,21 @@ namespace Umbraco.Core.Persistence.Querying
/// <returns>This instance so calls to this method are chainable</returns>
public virtual IQuery<T> Where(Expression<Func<T, bool>> predicate)
{
if (predicate != null)
{
//TODO: This should have an SqlSyntax object passed in, this ctor is relying on a singleton
var expressionHelper = new ModelToSqlExpressionVisitor<T>(_sqlSyntax, _mappers);
string whereExpression = expressionHelper.Visit(predicate);
if (predicate == null) return this;
var expressionHelper = new ModelToSqlExpressionVisitor<T>(_sqlSyntax, _mappers);
var whereExpression = expressionHelper.Visit(predicate);
_wheres.Add(new Tuple<string, object[]>(whereExpression, expressionHelper.GetSqlParameters()));
}
return this;
}
public virtual IQuery<T> WhereIn(Expression<Func<T, object>> fieldSelector, IEnumerable values)
{
if (fieldSelector != null)
{
var expressionHelper = new ModelToSqlExpressionVisitor<T>(_sqlSyntax, _mappers);
string whereExpression = expressionHelper.Visit(fieldSelector);
if (fieldSelector == null) return this;
_wheres.Add(new Tuple<string, object[]>(whereExpression + " IN (@values)", new object[] { new { @values = values } }));
}
var expressionHelper = new ModelToSqlExpressionVisitor<T>(_sqlSyntax, _mappers);
var whereExpression = expressionHelper.Visit(fieldSelector);
_wheres.Add(new Tuple<string, object[]>(whereExpression + " IN (@values)", new object[] { new { values } }));
return this;
}

View File

@@ -15,8 +15,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
internal class AuditRepository : NPocoRepositoryBase<int, AuditItem>, IAuditRepository
{
public AuditRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public AuditRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -32,8 +32,8 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly CacheHelper _cacheHelper;
private PermissionRepository<IContent> _permissionRepository;
public ContentRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection, IQueryFactory queryFactory)
: base(work, cacheHelper, logger, contentSection, queryFactory)
public ContentRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection)
: base(work, cacheHelper, logger, contentSection)
{
if (contentTypeRepository == null) throw new ArgumentNullException(nameof(contentTypeRepository));
if (templateRepository == null) throw new ArgumentNullException(nameof(templateRepository));
@@ -43,7 +43,7 @@ namespace Umbraco.Core.Persistence.Repositories
_tagRepository = tagRepository;
_cacheHelper = cacheHelper;
_publishedQuery = queryFactory.Create<IContent>().Where(x => x.Published);
_publishedQuery = work.DatabaseContext.Query<IContent>().Where(x => x.Published);
EnsureUniqueNaming = true;
}

View File

@@ -22,8 +22,8 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly ITemplateRepository _templateRepository;
private IRepositoryCachePolicy<IContentType, int> _cachePolicy;
public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ITemplateRepository templateRepository, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ITemplateRepository templateRepository)
: base(work, cache, logger)
{
_templateRepository = templateRepository;
}

View File

@@ -31,8 +31,8 @@ namespace Umbraco.Core.Persistence.Repositories
internal abstract class ContentTypeRepositoryBase<TEntity> : NPocoRepositoryBase<int, TEntity>, IReadRepository<Guid, TEntity>
where TEntity : class, IContentTypeComposition
{
protected ContentTypeRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
protected ContentTypeRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{ }
public IEnumerable<MoveEventInfo<TEntity>> Move(TEntity moving, EntityContainer container)

View File

@@ -7,8 +7,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
class DataTypeContainerRepository : EntityContainerRepository, IDataTypeContainerRepository
{
public DataTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(uow, cache, logger, queryFactory, Constants.ObjectTypes.DataTypeContainerGuid)
public DataTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger)
: base(uow, cache, logger, Constants.ObjectTypes.DataTypeContainerGuid)
{ }
}
}

View File

@@ -31,11 +31,11 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly IContentTypeRepository _contentTypeRepository;
private readonly DataTypePreValueRepository _preValRepository;
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentTypeRepository contentTypeRepository, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentTypeRepository contentTypeRepository)
: base(work, cache, logger)
{
_contentTypeRepository = contentTypeRepository;
_preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, queryFactory);
_preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger);
}
#region Overrides of RepositoryBase<int,DataTypeDefinition>
@@ -533,8 +533,8 @@ AND umbracoNode.id <> @id",
/// </summary>
private class DataTypePreValueRepository : NPocoRepositoryBase<int, PreValueEntity>
{
public DataTypePreValueRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public DataTypePreValueRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -22,8 +22,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private IRepositoryCachePolicy<IDictionaryItem, int> _cachePolicy;
public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{ }
protected override IRepositoryCachePolicy<IDictionaryItem, int> CachePolicy
@@ -240,13 +240,13 @@ namespace Umbraco.Core.Persistence.Repositories
public IDictionaryItem Get(Guid uniqueId)
{
var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, UnitOfWork, RepositoryCache, Logger, QueryFactory);
var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, UnitOfWork, Cache, Logger);
return uniqueIdRepo.Get(uniqueId);
}
public IDictionaryItem Get(string key)
{
var keyRepo = new DictionaryByKeyRepository(this, UnitOfWork, RepositoryCache, Logger, QueryFactory);
var keyRepo = new DictionaryByKeyRepository(this, UnitOfWork, Cache, Logger);
return keyRepo.Get(key);
}
@@ -294,8 +294,8 @@ namespace Umbraco.Core.Persistence.Repositories
private IRepositoryCachePolicy<IDictionaryItem, Guid> _cachePolicy;
private readonly DictionaryRepository _dictionaryRepository;
public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
_dictionaryRepository = dictionaryRepository;
}
@@ -355,8 +355,8 @@ namespace Umbraco.Core.Persistence.Repositories
private IRepositoryCachePolicy<IDictionaryItem, string> _cachePolicy;
private readonly DictionaryRepository _dictionaryRepository;
public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
_dictionaryRepository = dictionaryRepository;
}

View File

@@ -1,6 +1,5 @@
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -8,8 +7,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
class DocumentTypeContainerRepository : EntityContainerRepository, IDocumentTypeContainerRepository
{
public DocumentTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(uow, cache, logger, queryFactory, Constants.ObjectTypes.DocumentTypeContainerGuid)
public DocumentTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger)
: base(uow, cache, logger, Constants.ObjectTypes.DocumentTypeContainerGuid)
{ }
}
}

View File

@@ -18,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private IRepositoryCachePolicy<IDomain, int> _cachePolicy;
public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -22,8 +22,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private readonly Guid _containerObjectType;
public EntityContainerRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory, Guid containerObjectType)
: base(work, cache, logger, queryFactory)
public EntityContainerRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, Guid containerObjectType)
: base(work, cache, logger)
{
var allowedContainers = new[] {Constants.ObjectTypes.DocumentTypeContainerGuid, Constants.ObjectTypes.MediaTypeContainerGuid, Constants.ObjectTypes.DataTypeContainerGuid};
_containerObjectType = containerObjectType;

View File

@@ -19,12 +19,9 @@ namespace Umbraco.Core.Persistence.Repositories
/// </remarks>
internal class EntityRepository : DisposableObject, IEntityRepository
{
private readonly IQueryFactory _queryFactory;
public EntityRepository(IDatabaseUnitOfWork work, IQueryFactory queryFactory)
public EntityRepository(IDatabaseUnitOfWork work)
{
UnitOfWork = work;
_queryFactory = queryFactory;
}
/// <summary>
@@ -34,7 +31,7 @@ namespace Umbraco.Core.Persistence.Repositories
#region Query Methods
public IQuery<IUmbracoEntity> Query => _queryFactory.Create<IUmbracoEntity>();
public IQuery<IUmbracoEntity> Query => UnitOfWork.DatabaseContext.Query<IUmbracoEntity>();
public Sql<SqlContext> Sql() { return UnitOfWork.Database.Sql();}

View File

@@ -16,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
internal class ExternalLoginRepository : NPocoRepositoryBase<int, IIdentityUserLogin>, IExternalLoginRepository
{
public ExternalLoginRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public ExternalLoginRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IAuditRepository : IRepositoryQueryable<int, AuditItem>
public interface IAuditRepository : IQueryRepository<int, AuditItem>
{
void CleanLogs(int maximumAgeOfLogsInMinutes);
}

View File

@@ -5,7 +5,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentTypeRepositoryBase<TItem> : IRepositoryQueryable<int, TItem>, IReadRepository<Guid, TItem>
public interface IContentTypeRepositoryBase<TItem> : IQueryRepository<int, TItem>, IReadRepository<Guid, TItem>
where TItem : IContentTypeComposition
{
TItem Get(string alias);

View File

@@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IDataTypeDefinitionRepository : IRepositoryQueryable<int, IDataTypeDefinition>
public interface IDataTypeDefinitionRepository : IQueryRepository<int, IDataTypeDefinition>
{
PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId);

View File

@@ -4,7 +4,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IDictionaryRepository : IRepositoryQueryable<int, IDictionaryItem>
public interface IDictionaryRepository : IQueryRepository<int, IDictionaryItem>
{
IDictionaryItem Get(Guid uniqueId);
IDictionaryItem Get(string key);

View File

@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IDomainRepository : IRepositoryQueryable<int, IDomain>
public interface IDomainRepository : IQueryRepository<int, IDomain>
{
IDomain GetByName(string domainName);
bool Exists(string domainName);

View File

@@ -4,7 +4,7 @@ using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IExternalLoginRepository : IRepositoryQueryable<int, IIdentityUserLogin>
public interface IExternalLoginRepository : IQueryRepository<int, IIdentityUserLogin>
{
void SaveUserLogins(int memberId, IEnumerable<UserLoginInfo> logins);
void DeleteUserLogins(int memberId);

View File

@@ -2,7 +2,7 @@
namespace Umbraco.Core.Persistence.Repositories
{
public interface ILanguageRepository : IRepositoryQueryable<int, ILanguage>
public interface ILanguageRepository : IQueryRepository<int, ILanguage>
{
ILanguage GetByCultureName(string cultureName);
ILanguage GetByIsoCode(string isoCode);

View File

@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
internal interface IMacroRepository : IRepositoryQueryable<int, IMacro>
internal interface IMacroRepository : IQueryRepository<int, IMacro>
{
//IEnumerable<IMacro> GetAll(params string[] aliases);

View File

@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IMemberGroupRepository : IRepositoryQueryable<int, IMemberGroup>
public interface IMemberGroupRepository : IQueryRepository<int, IMemberGroup>
{
/// <summary>
/// Gets a member group by it's name

View File

@@ -4,7 +4,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IMigrationEntryRepository : IRepositoryQueryable<int, IMigrationEntry>
public interface IMigrationEntryRepository : IQueryRepository<int, IMigrationEntry>
{
IMigrationEntry FindEntry(string migrationName, SemVersion version);
}

View File

@@ -5,7 +5,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IPublicAccessRepository : IRepositoryQueryable<Guid, PublicAccessEntry>
public interface IPublicAccessRepository : IQueryRepository<Guid, PublicAccessEntry>
{
}
}

View File

@@ -6,12 +6,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
//TODO: This should be decoupled! Shouldn't inherit from the IRepository
/// <summary>
/// Defines the implementation of a Repository, which allows queries against the <see cref="TEntity"/>
/// </summary>
/// <typeparam name="TEntity">Type of <see cref="IAggregateRoot"/> entity for which the repository is used</typeparam>
/// <typeparam name="TId">Type of the Id used for this entity</typeparam>
public interface IRepositoryQueryable<in TId, TEntity> : IRepository<TId, TEntity>
public interface IQueryRepository<in TId, TEntity> : IRepository<TId, TEntity>
{
/// <summary>
/// Returns a query instance

View File

@@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Defines the <see cref="IRedirectUrl"/> repository.
/// </summary>
public interface IRedirectUrlRepository : IRepositoryQueryable<Guid, IRedirectUrl>
public interface IRedirectUrlRepository : IQueryRepository<Guid, IRedirectUrl>
{
/// <summary>
/// Gets a redirect url.

View File

@@ -2,7 +2,7 @@
namespace Umbraco.Core.Persistence.Repositories
{
public interface IRelationRepository : IRepositoryQueryable<int, IRelation>
public interface IRelationRepository : IQueryRepository<int, IRelation>
{
}

View File

@@ -2,7 +2,7 @@
namespace Umbraco.Core.Persistence.Repositories
{
public interface IRelationTypeRepository : IRepositoryQueryable<int, IRelationType>
public interface IRelationTypeRepository : IQueryRepository<int, IRelationType>
{
}

View File

@@ -1,19 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Defines the base implementation of a Repository
/// Defines the base implementation of a repository.
/// </summary>
/// <remarks>
/// Currently this interface is empty but it is useful for flagging a repository without having generic parameters, it also might
/// come in handy if we need to add anything to the base/non-generic repository interface.
/// </remarks>
public interface IRepository
{
}
{ }
public interface IReadRepository<in TId, out TEntity> : IRepository
{
@@ -39,12 +32,7 @@ namespace Umbraco.Core.Persistence.Repositories
bool Exists(TId id);
}
//TODO: This should be decoupled! Shouldn't inherit from the IReadRepository and should be named IWriteRepository
/// <summary>
/// Defines the implementation of a Repository
/// </summary>
public interface IRepository<in TId, TEntity> : IReadRepository<TId, TEntity>
public interface IWriteRepository<in TEntity> : IRepository
{
/// <summary>
/// Adds or Updates an Entity
@@ -58,4 +46,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="entity"></param>
void Delete(TEntity entity);
}
public interface IRepository<in TId, TEntity> : IReadRepository<TId, TEntity>, IWriteRepository<TEntity>
{ }
}

View File

@@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <typeparam name="TEntity">Type of <see cref="IAggregateRoot"/> entity for which the repository is used</typeparam>
/// <typeparam name="TId">Type of the Id used for this entity</typeparam>
public interface IRepositoryVersionable<TId, TEntity> : IRepositoryQueryable<TId, TEntity>
public interface IRepositoryVersionable<TId, TEntity> : IQueryRepository<TId, TEntity>
where TEntity : IAggregateRoot
{
/// <summary>

View File

@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IServerRegistrationRepository : IRepositoryQueryable<int, IServerRegistration>
public interface IServerRegistrationRepository : IQueryRepository<int, IServerRegistration>
{
void DeactiveStaleServers(TimeSpan staleTimeout);
}

View File

@@ -4,7 +4,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface ITagRepository : IRepositoryQueryable<int, ITag>
public interface ITagRepository : IQueryRepository<int, ITag>
{
TaggedEntity GetTaggedEntityByKey(Guid key);

View File

@@ -4,7 +4,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface ITaskRepository : IRepositoryQueryable<int, Task>
public interface ITaskRepository : IQueryRepository<int, Task>
{
IEnumerable<Task> GetTasks(int? itemId = null, int? assignedUser = null, int? ownerUser = null, string taskTypeAlias = null, bool includeClosed = false);
}

View File

@@ -2,7 +2,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface ITaskTypeRepository : IRepositoryQueryable<int, TaskType>
public interface ITaskTypeRepository : IQueryRepository<int, TaskType>
{
}

View File

@@ -6,7 +6,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface ITemplateRepository : IRepositoryQueryable<int, ITemplate>
public interface ITemplateRepository : IQueryRepository<int, ITemplate>
{
ITemplate Get(string alias);

View File

@@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IUserRepository : IRepositoryQueryable<int, IUser>
public interface IUserRepository : IQueryRepository<int, IUser>
{
/// <summary>
/// Gets the count of items based on a complex query

View File

@@ -2,7 +2,7 @@
namespace Umbraco.Core.Persistence.Repositories
{
public interface IUserTypeRepository : IRepositoryQueryable<int, IUserType>
public interface IUserTypeRepository : IQueryRepository<int, IUserType>
{
}

View File

@@ -21,8 +21,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private IRepositoryCachePolicy<ILanguage, int> _cachePolicy;
public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -17,8 +17,8 @@ namespace Umbraco.Core.Persistence.Repositories
internal class MacroRepository : NPocoRepositoryBase<int, IMacro>, IMacroRepository
{
public MacroRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public MacroRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -25,8 +25,8 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly IMediaTypeRepository _mediaTypeRepository;
private readonly ITagRepository _tagRepository;
public MediaRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, IQueryFactory queryFactory)
: base(work, cache, logger, contentSection, queryFactory)
public MediaRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection)
: base(work, cache, logger, contentSection)
{
if (mediaTypeRepository == null) throw new ArgumentNullException(nameof(mediaTypeRepository));
if (tagRepository == null) throw new ArgumentNullException(nameof(tagRepository));

View File

@@ -1,6 +1,5 @@
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -8,8 +7,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
class MediaTypeContainerRepository : EntityContainerRepository, IMediaTypeContainerRepository
{
public MediaTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(uow, cache, logger, queryFactory, Constants.ObjectTypes.MediaTypeContainerGuid)
public MediaTypeContainerRepository(IDatabaseUnitOfWork uow, CacheHelper cache, ILogger logger)
: base(uow, cache, logger, Constants.ObjectTypes.MediaTypeContainerGuid)
{ }
}
}

View File

@@ -18,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private IRepositoryCachePolicy<IMediaType, int> _cachePolicy;
public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{ }
protected override IRepositoryCachePolicy<IMediaType, int> CachePolicy

View File

@@ -17,8 +17,8 @@ namespace Umbraco.Core.Persistence.Repositories
internal class MemberGroupRepository : NPocoRepositoryBase<int, IMemberGroup>, IMemberGroupRepository
{
public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{ }
private readonly MemberGroupFactory _modelFactory = new MemberGroupFactory();

View File

@@ -30,8 +30,8 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly ITagRepository _tagRepository;
private readonly IMemberGroupRepository _memberGroupRepository;
public MemberRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, IContentSection contentSection, IQueryFactory queryFactory)
: base(work, cache, logger, contentSection, queryFactory)
public MemberRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, IContentSection contentSection)
: base(work, cache, logger, contentSection)
{
if (memberTypeRepository == null) throw new ArgumentNullException(nameof(memberTypeRepository));
if (tagRepository == null) throw new ArgumentNullException(nameof(tagRepository));

View File

@@ -21,8 +21,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private IRepositoryCachePolicy<IMemberType, int> _cachePolicy;
public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{ }
protected override IRepositoryCachePolicy<IMemberType, int> CachePolicy

View File

@@ -17,8 +17,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
internal class MigrationEntryRepository : NPocoRepositoryBase<int, IMigrationEntry>, IMigrationEntryRepository
{
public MigrationEntryRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public MigrationEntryRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -24,11 +24,10 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="work">A database unit of work.</param>
/// <param name="cache">A cache helper.</param>
/// <param name="logger">A logger.</param>
/// <param name="queryFactory">A query factory.</param>
protected NPocoRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
protected NPocoRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
QueryFactory = queryFactory;
QueryFactory = work.DatabaseContext.QueryFactory;
}
/// <summary>
@@ -44,7 +43,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Gets the repository's database sql syntax.
/// </summary>
public ISqlSyntaxProvider SqlSyntax => Database.SqlSyntax;
public ISqlSyntaxProvider SqlSyntax => UnitOfWork.DatabaseContext.SqlSyntax;
/// <summary>
/// Gets the repository's query factory.
@@ -61,10 +60,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// Creates a new Sql statement.
/// </summary>
/// <returns>A new Sql statement.</returns>
protected Sql<SqlContext> Sql()
{
return Database.Sql();
}
protected Sql<SqlContext> Sql() => UnitOfWork.DatabaseContext.Sql();
#region Abstract Methods
@@ -103,7 +99,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
protected virtual new TId GetEntityId(TEntity entity)
protected new virtual TId GetEntityId(TEntity entity)
{
return (TId)(object) entity.Id;
}

View File

@@ -16,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private IRepositoryCachePolicy<PublicAccessEntry, Guid> _cachePolicy;
public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{ }
protected override IRepositoryCachePolicy<PublicAccessEntry, Guid> CachePolicy

View File

@@ -13,8 +13,8 @@ namespace Umbraco.Core.Persistence.Repositories
where TEntity : class, IUmbracoEntity
where TRepository : class, IRepository
{
protected RecycleBinRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentSection contentSection, IQueryFactory queryFactory)
: base(work, cache, logger, contentSection, queryFactory)
protected RecycleBinRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentSection contentSection)
: base(work, cache, logger, contentSection)
{
}

View File

@@ -13,8 +13,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
internal class RedirectUrlRepository : NPocoRepositoryBase<Guid, IRedirectUrl>, IRedirectUrlRepository
{
public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{ }
protected override int PerformCount(IQuery<IRedirectUrl> query)

View File

@@ -23,8 +23,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private readonly IRelationTypeRepository _relationTypeRepository;
public RelationRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IRelationTypeRepository relationTypeRepository, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public RelationRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IRelationTypeRepository relationTypeRepository)
: base(work, cache, logger)
{
_relationTypeRepository = relationTypeRepository;
}

View File

@@ -20,8 +20,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private IRepositoryCachePolicy<IRelationType, int> _cachePolicy;
public RelationTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public RelationTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{ }
protected override IRepositoryCachePolicy<IRelationType, int> CachePolicy

View File

@@ -10,8 +10,13 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Provides a base class to all repositories.
/// </summary>
internal abstract class RepositoryBase
{
private static readonly Dictionary<Type, string> CacheTypeKeys = new Dictionary<Type, string>();
protected RepositoryBase(IUnitOfWork work, CacheHelper cache, ILogger logger)
{
if (work == null) throw new ArgumentNullException(nameof(work));
@@ -19,46 +24,42 @@ namespace Umbraco.Core.Persistence.Repositories
if (logger == null) throw new ArgumentNullException(nameof(logger));
Logger = logger;
UnitOfWork = work;
RepositoryCache = cache;
Cache = cache;
}
/// <summary>
/// Returns the Unit of Work added to the repository
/// </summary>
protected internal IUnitOfWork UnitOfWork { get; }
protected IUnitOfWork UnitOfWork { get; }
protected CacheHelper RepositoryCache { get; }
protected CacheHelper Cache { get; }
/// <summary>
/// The runtime cache used for this repo - by standard this is the runtime cache exposed by the CacheHelper but can be overridden
/// </summary>
protected virtual IRuntimeCacheProvider RuntimeCache => RepositoryCache.RuntimeCache;
protected virtual IRuntimeCacheProvider RuntimeCache => Cache.RuntimeCache;
public static string GetCacheIdKey<T>(object id)
{
return $"{GetCacheTypeKey<T>()}{id}";
}
protected ILogger Logger { get; }
public static string GetCacheIdKey<T>(object id) => GetCacheTypeKey<T>() + id;
public static string GetCacheTypeKey<T>()
{
return $"uRepo_{typeof (T).Name}_";
string key;
var type = typeof (T);
return CacheTypeKeys.TryGetValue(type, out key) ? key : (CacheTypeKeys[type] = "uRepo_" + type.Name + "_");
}
protected ILogger Logger { get; private set; }
}
/// <summary>
/// Represent an abstract Repository, which is the base of the Repository implementations
/// Provides a base class to all repositories.
/// </summary>
/// <typeparam name="TEntity">Type of <see cref="IAggregateRoot"/> entity for which the repository is used</typeparam>
/// <typeparam name="TId">Type of the Id used for this entity</typeparam>
internal abstract class RepositoryBase<TId, TEntity> : RepositoryBase, IRepositoryQueryable<TId, TEntity>, IUnitOfWorkRepository
/// <typeparam name="TEntity">The type of the entity managed by this repository.</typeparam>
/// <typeparam name="TId">The type of the entity's unique identifier.</typeparam>
internal abstract class RepositoryBase<TId, TEntity> : RepositoryBase, IReadRepository<TId, TEntity>, IWriteRepository<TEntity>, IQueryRepository<TId, TEntity>, IUnitOfWorkRepository
where TEntity : class, IAggregateRoot
{
private IRepositoryCachePolicy<TEntity, TId> _cachePolicy;
protected RepositoryBase(IUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}
{ }
protected override IRuntimeCacheProvider RuntimeCache => Cache.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
/// <summary>
/// Used to create a new query instance
@@ -79,37 +80,17 @@ namespace Umbraco.Core.Persistence.Repositories
protected virtual TId GetEntityId(TEntity entity)
{
return (TId)(object)entity.Id;
return (TId) (object) entity.Id;
}
/// <summary>
/// The runtime cache used for this repo by default is the isolated cache for this type
/// </summary>
protected override IRuntimeCacheProvider RuntimeCache => RepositoryCache.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
private IRepositoryCachePolicy<TEntity, TId> _cachePolicy;
protected virtual IRepositoryCachePolicy<TEntity, TId> CachePolicy
{
get
{
if (_cachePolicy != null) return _cachePolicy;
var options = new RepositoryCachePolicyOptions(() =>
{
//create it once if it is needed (no need for locking here)
if (_hasIdQuery == null)
{
_hasIdQuery = Query.Where(x => x.Id != 0);
}
//Get count of all entities of current type (TEntity) to ensure cached result is correct
return PerformCount(_hasIdQuery);
});
_cachePolicy = new DefaultRepositoryCachePolicy<TEntity, TId>(RuntimeCache, options);
return _cachePolicy;
if (_hasIdQuery == null) _hasIdQuery = Query.Where(x => x.Id != 0);
var options = new RepositoryCachePolicyOptions(() => PerformCount(_hasIdQuery));
return _cachePolicy = new DefaultRepositoryCachePolicy<TEntity, TId>(RuntimeCache, options);
}
set
{
@@ -125,14 +106,10 @@ namespace Umbraco.Core.Persistence.Repositories
public void AddOrUpdate(TEntity entity)
{
if (entity.HasIdentity == false)
{
UnitOfWork.RegisterCreated(entity, this);
}
else
{
UnitOfWork.RegisterUpdated(entity, this);
}
}
/// <summary>
/// Deletes the passed in entity
@@ -178,6 +155,12 @@ namespace Umbraco.Core.Persistence.Repositories
}
protected abstract IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query);
protected abstract bool PerformExists(TId id);
protected abstract int PerformCount(IQuery<TEntity> query);
protected abstract void PersistNewItem(TEntity item);
protected abstract void PersistUpdatedItem(TEntity item);
protected abstract void PersistDeletedItem(TEntity item);
/// <summary>
/// Gets a list of entities by the passed in query
/// </summary>
@@ -190,7 +173,6 @@ namespace Umbraco.Core.Persistence.Repositories
.WhereNotNull();
}
protected abstract bool PerformExists(TId id);
/// <summary>
/// Returns a boolean indicating whether an entity with the passed Id exists
/// </summary>
@@ -201,7 +183,6 @@ namespace Umbraco.Core.Persistence.Repositories
return CachePolicy.Exists(id, PerformExists, PerformGetAll);
}
protected abstract int PerformCount(IQuery<TEntity> query);
/// <summary>
/// Returns an integer with the count of entities found with the passed in query
/// </summary>
@@ -238,9 +219,5 @@ namespace Umbraco.Core.Persistence.Repositories
{
CachePolicy.Delete((TEntity) entity, PersistDeletedItem);
}
protected abstract void PersistNewItem(TEntity item);
protected abstract void PersistUpdatedItem(TEntity item);
protected abstract void PersistDeletedItem(TEntity item);
}
}

View File

@@ -16,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
private IRepositoryCachePolicy<IServerRegistration, int> _cachePolicy;
public ServerRegistrationRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IQueryFactory queryFactory)
: base(work, CacheHelper.CreateDisabledCacheHelper(), logger, queryFactory)
public ServerRegistrationRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger)
: base(work, CacheHelper.CreateDisabledCacheHelper(), logger)
{ }
protected override IRepositoryCachePolicy<IServerRegistration, int> CachePolicy => _cachePolicy

View File

@@ -18,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories
where TDto: class
{
protected SimpleGetRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
protected SimpleGetRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -16,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
internal class TagRepository : NPocoRepositoryBase<int, ITag>, ITagRepository
{
public TagRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public TagRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -16,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
internal class TaskRepository : NPocoRepositoryBase<int, Task>, ITaskRepository
{
public TaskRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public TaskRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -16,8 +16,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
internal class TaskTypeRepository : NPocoRepositoryBase<int, TaskType>, ITaskTypeRepository
{
public TaskTypeRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public TaskTypeRepository(IDatabaseUnitOfWork work, [Inject(RepositoryCompositionRoot.DisabledCache)] CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -37,8 +37,8 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly MasterPageHelper _masterPageHelper;
private IRepositoryCachePolicy<ITemplate, int> _cachePolicy;
public TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig)
: base(work, cache, logger)
{
_masterpagesFileSystem = masterpageFileSystem;
_viewsFileSystem = viewFileSystem;

View File

@@ -28,8 +28,8 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly CacheHelper _cacheHelper;
private PermissionRepository<IContent> _permissionRepository;
public UserRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IUserTypeRepository userTypeRepository, IQueryFactory queryFactory)
: base(work, cacheHelper, logger, queryFactory)
public UserRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IUserTypeRepository userTypeRepository)
: base(work, cacheHelper, logger)
{
_userTypeRepository = userTypeRepository;
_cacheHelper = cacheHelper;

View File

@@ -8,9 +8,7 @@ using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
@@ -20,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
internal class UserTypeRepository : NPocoRepositoryBase<int, IUserType>, IUserTypeRepository
{
public UserTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
public UserTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger)
: base(work, cache, logger)
{
}

View File

@@ -50,8 +50,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
//private readonly IContentSection _contentSection;
protected VersionableRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentSection contentSection, IQueryFactory queryFactory)
: base(work, cache, logger, queryFactory)
protected VersionableRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IContentSection contentSection)
: base(work, cache, logger)
{
//_contentSection = contentSection;
}

View File

@@ -26,6 +26,7 @@ namespace Umbraco.Core.Persistence
private const IsolationLevel DefaultIsolationLevel = IsolationLevel.RepeatableRead;
private readonly ILogger _logger;
private readonly SqlContext _sqlContext;
private readonly RetryPolicy _connectionRetryPolicy;
private readonly RetryPolicy _commandRetryPolicy;
private bool _enableCount;
@@ -35,7 +36,7 @@ namespace Umbraco.Core.Persistence
/// </summary>
internal Guid InstanceId { get; } = Guid.NewGuid();
public ISqlSyntaxProvider SqlSyntax { get; }
public ISqlSyntaxProvider SqlSyntax => _sqlContext.SqlSyntax;
/// <summary>
/// Generally used for testing, will output all SQL statements executed to the logger
@@ -68,38 +69,40 @@ namespace Umbraco.Core.Persistence
// creates one instance per request
// also used by DatabaseContext for creating DBs and upgrading
public UmbracoDatabase(string connectionString,
ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, DbProviderFactory provider,
ILogger logger,
SqlContext sqlContext, DbProviderFactory provider, ILogger logger,
RetryPolicy connectionRetryPolicy = null, RetryPolicy commandRetryPolicy = null)
: base(connectionString, databaseType, provider, DefaultIsolationLevel)
: base(connectionString, sqlContext.DatabaseType, provider, DefaultIsolationLevel)
{
SqlSyntax = sqlSyntax;
_sqlContext = sqlContext;
_logger = logger;
_connectionRetryPolicy = connectionRetryPolicy;
_commandRetryPolicy = commandRetryPolicy;
EnableSqlTrace = false;
}
// INTERNAL FOR UNIT TESTS
internal UmbracoDatabase(DbConnection connection,
ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType,
ILogger logger)
ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, ILogger logger)
: base(connection, databaseType, DefaultIsolationLevel)
{
SqlSyntax = sqlSyntax;
_sqlContext = new SqlContext(sqlSyntax, null, databaseType); // beware! no pocoDataFactory!
_logger = logger;
EnableSqlTrace = false;
}
// fixme: these two could be an extension method of IUmbracoDatabaseConfig
public Sql<SqlContext> Sql()
{
return NPoco.Sql.BuilderFor(new SqlContext(this));
return NPoco.Sql.BuilderFor(_sqlContext);
}
public Sql<SqlContext> Sql(string sql, params object[] args)
{
return NPoco.Sql.BuilderFor(new SqlContext(this)).Append(sql, args);
return Sql().Append(sql, args);
}
//protected override void OnConnectionClosing(DbConnection conn)
@@ -159,5 +162,7 @@ namespace Umbraco.Core.Persistence
}
base.OnExecutedCommand(cmd);
}
// fixme - see v7.6 - what about disposing & managing context and call context?
}
}

View File

@@ -11,10 +11,10 @@
/// <summary>
/// Initializes a new instance of the <see cref="NPocoUnitOfWork"/> class with a a repository factory.
/// </summary>
/// <param name="factory">A repository factory.</param>
/// <param name="repositoryFactory">A repository factory.</param>
/// <remarks>This should be used by the FileUnitOfWorkProvider exclusively.</remarks>
public FileUnitOfWork(RepositoryFactory factory)
: base(factory)
public FileUnitOfWork(RepositoryFactory repositoryFactory)
: base(repositoryFactory)
{ }
/// <summary>
@@ -25,7 +25,7 @@
/// <returns>The created repository for the unit of work.</returns>
public override TRepository CreateRepository<TRepository>(string name = null)
{
return Factory.CreateRepository<TRepository>(this, name);
return RepositoryFactory.CreateRepository<TRepository>(this, name);
}
}
}

View File

@@ -5,9 +5,26 @@ namespace Umbraco.Core.Persistence.UnitOfWork
/// </summary>
public interface IDatabaseUnitOfWork : IUnitOfWork
{
/// <summary>
/// Gets the database context.
/// </summary>
DatabaseContext DatabaseContext { get; }
/// <summary>
/// Gets the current database instance.
/// </summary>
UmbracoDatabase Database { get; }
/// <summary>
/// Read-locks some lock objects.
/// </summary>
/// <param name="lockIds">The lock object identifiers.</param>
void ReadLock(params int[] lockIds);
/// <summary>
/// Write-locks some lock objects.
/// </summary>
/// <param name="lockIds">The lock object identifiers.</param>
void WriteLock(params int[] lockIds);
}
}

View File

@@ -5,6 +5,11 @@ namespace Umbraco.Core.Persistence.UnitOfWork
/// </summary>
public interface IDatabaseUnitOfWorkProvider
{
/// <summary>
/// Gets the database context.
/// </summary>
DatabaseContext DatabaseContext { get; }
/// <summary>
/// Creates a unit of work.
/// </summary>

View File

@@ -14,36 +14,30 @@ namespace Umbraco.Core.Persistence.UnitOfWork
/// <summary>
/// Initializes a new instance of the <see cref="NPocoUnitOfWork"/> class with a database and a repository factory.
/// </summary>
/// <param name="databaseContext">The database context.</param>
/// <param name="database">A database.</param>
/// <param name="factory">A repository factory.</param>
/// <param name="repositoryFactory">A repository factory.</param>
/// <remarks>This should be used by the NPocoUnitOfWorkProvider exclusively.</remarks>
internal NPocoUnitOfWork(UmbracoDatabase database, RepositoryFactory factory)
: base(factory)
internal NPocoUnitOfWork(DatabaseContext databaseContext, UmbracoDatabase database, RepositoryFactory repositoryFactory)
: base(repositoryFactory)
{
DatabaseContext = databaseContext;
Database = database;
}
/// <summary>
/// Gets the unit of work underlying database.
/// </summary>
public UmbracoDatabase Database { get; }
/// <inheritdoc />
public DatabaseContext DatabaseContext { get; }
/// <summary>
/// Creates a repository.
/// </summary>
/// <typeparam name="TRepository">The type of the repository.</typeparam>
/// <param name="name">The optional name of the repository.</param>
/// <returns>The created repository for the unit of work.</returns>
/// <inheritdoc />
public UmbracoDatabase Database { get; } // => DatabaseContext.Database; // fixme + change ctor
/// <inheritdoc />
public override TRepository CreateRepository<TRepository>(string name = null)
{
return Factory.CreateRepository<TRepository>(this, name);
return RepositoryFactory.CreateRepository<TRepository>(this, name);
}
/// <summary>
/// Ensures that we have a transaction.
/// </summary>
/// <remarks>Isolation level is determined by the database, see UmbracoDatabase.DefaultIsolationLevel. Should be
/// at least IsolationLevel.RepeatablRead else the node locks will not work correctly.</remarks>
/// <inheritdoc />
public override void Begin()
{
base.Begin();
@@ -52,6 +46,39 @@ namespace Umbraco.Core.Persistence.UnitOfWork
_transaction = Database.GetTransaction();
}
/// <inheritdoc />
public void ReadLock(params int[] lockIds)
{
Begin(); // we need a transaction
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = Database.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId });
if (i == null) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
/// <inheritdoc />
public void WriteLock(params int[] lockIds)
{
Begin(); // we need a transaction
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = Database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId });
if (i == 0) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
/// <inheritdoc />
protected override void DisposeResources()
{
base.DisposeResources();
@@ -69,37 +96,5 @@ namespace Umbraco.Core.Persistence.UnitOfWork
_transaction = null;
}
public void ReadLock(params int[] lockIds)
{
Begin(); // we need a transaction
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = Database.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id",
new { @id = lockId });
if (i == null) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
public void WriteLock(params int[] lockIds)
{
Begin(); // we need a transaction
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = Database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id",
new { @id = lockId });
if (i == 0) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
}
}

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.DI;
namespace Umbraco.Core.Persistence.UnitOfWork
{
@@ -28,35 +23,18 @@ namespace Umbraco.Core.Persistence.UnitOfWork
_repositoryFactory = repositoryFactory;
}
// this should NOT be here, all tests should supply the appropriate providers,
// however the above ctor is used in hundreds of tests at the moment, so...
// will refactor later
private static IEnumerable<ISqlSyntaxProvider> GetDefaultSqlSyntaxProviders(ILogger logger)
{
return new ISqlSyntaxProvider[]
{
new MySqlSyntaxProvider(logger),
new SqlCeSyntaxProvider(),
new SqlServerSyntaxProvider(new Lazy<IDatabaseFactory>(() => null))
};
}
/// <inheritdoc />
public DatabaseContext DatabaseContext => Current.DatabaseContext; // fixme inject!
#region Implement IUnitOfWorkProvider
/// <summary>
/// Creates a unit of work around a database obtained from the database factory.
/// </summary>
/// <returns>A unit of work.</returns>
/// <remarks>The unit of work will execute on the database returned by the database factory.</remarks>
/// <inheritdoc />
public IDatabaseUnitOfWork CreateUnitOfWork()
{
// get a database from the factory - might be the "ambient" database eg
// the one that's enlisted with the HttpContext - so it's not always a
// the one that's enlisted with the HttpContext - so it's *not* necessary a
// "new" database.
var database = _databaseFactory.GetDatabase();
return new NPocoUnitOfWork(database, _repositoryFactory);
var databaseContext = Current.DatabaseContext; // fixme - inject!
return new NPocoUnitOfWork(databaseContext, database, _repositoryFactory);
}
#endregion
}
}

View File

@@ -9,12 +9,12 @@ namespace Umbraco.Core.Persistence.UnitOfWork
{
private readonly Queue<Operation> _operations = new Queue<Operation>();
protected UnitOfWorkBase(RepositoryFactory factory)
protected UnitOfWorkBase(RepositoryFactory repositoryFactory)
{
Factory = factory;
RepositoryFactory = repositoryFactory;
}
protected RepositoryFactory Factory { get; }
protected RepositoryFactory RepositoryFactory { get; }
public abstract TRepository CreateRepository<TRepository>(string name = null)
where TRepository : IRepository;

View File

@@ -21,6 +21,7 @@ namespace Umbraco.Core.Services
public class ContentService : RepositoryService, IContentService, IContentServiceOperations
{
private readonly MediaFileSystem _mediaFileSystem;
private IQuery<IContent> _queryNotTrashed;
#region Constructors
@@ -28,19 +29,19 @@ namespace Umbraco.Core.Services
IDatabaseUnitOfWorkProvider provider,
ILogger logger,
IEventMessagesFactory eventMessagesFactory,
IQueryFactory queryFactory,
MediaFileSystem mediaFileSystem)
: base(provider, logger, eventMessagesFactory)
{
_mediaFileSystem = mediaFileSystem;
_notTrashedQuery = queryFactory.Create<IContent>().Where(x => x.Trashed == false);
}
#endregion
#region Static Queries
#region Static queries
private readonly IQuery<IContent> _notTrashedQuery;
// lazy-constructed because when the ctor runs, the query factory may not be ready
private IQuery<IContent> QueryNotTrashed => _queryNotTrashed ?? (_queryNotTrashed = UowProvider.DatabaseContext.Query<IContent>().Where(x => x.Trashed == false));
#endregion
@@ -834,7 +835,7 @@ namespace Umbraco.Core.Services
{
uow.ReadLock(Constants.Locks.ContentTree);
var repository = uow.CreateRepository<IContentRepository>();
var content = repository.GetByPublishedVersion(_notTrashedQuery);
var content = repository.GetByPublishedVersion(QueryNotTrashed);
uow.Complete();
return content;
}

View File

@@ -20,18 +20,17 @@ namespace Umbraco.Core.Services
{
private readonly IRuntimeCacheProvider _runtimeCache;
private readonly Dictionary<string, Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>> _supportedObjectTypes;
private IQuery<IUmbracoEntity> _queryRootEntity;
public EntityService(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory,
IContentService contentService, IContentTypeService contentTypeService,
IMediaService mediaService, IMediaTypeService mediaTypeService,
IDataTypeService dataTypeService,
IMemberService memberService, IMemberTypeService memberTypeService,
IQueryFactory queryFactory,
IRuntimeCacheProvider runtimeCache)
: base(provider, logger, eventMessagesFactory)
{
_runtimeCache = runtimeCache;
_rootEntityQuery = queryFactory.Create<IUmbracoEntity>().Where(x => x.ParentId == -1);
_supportedObjectTypes = new Dictionary<string, Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>>
{
@@ -69,7 +68,9 @@ namespace Umbraco.Core.Services
#region Static Queries
private readonly IQuery<IUmbracoEntity> _rootEntityQuery;
// lazy-constructed because when the ctor runs, the query factory may not be ready
private IQuery<IUmbracoEntity> QueryRootEntity => _queryRootEntity ?? (_queryRootEntity = UowProvider.DatabaseContext.Query<IUmbracoEntity>().Where(x => x.ParentId == -1));
#endregion
@@ -98,7 +99,7 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.DataType:
case UmbracoObjectTypes.DocumentTypeContainer:
id = uow.Database.ExecuteScalar<int?>(
uow.Database.Sql()
uow.DatabaseContext.Sql()
.Select("id")
.From<NodeDto>()
.Where<NodeDto>(dto => dto.UniqueId == key));
@@ -144,7 +145,7 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
guid = uow.Database.ExecuteScalar<Guid?>(
uow.Database.Sql()
uow.DatabaseContext.Sql()
.Select("uniqueID")
.From<NodeDto>()
.Where<NodeDto>(dto => dto.NodeId == id));
@@ -442,7 +443,7 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
var entities = repository.GetByQuery(_rootEntityQuery, objectTypeId);
var entities = repository.GetByQuery(QueryRootEntity, objectTypeId);
uow.Complete();
return entities;
}
@@ -547,7 +548,7 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var sql = uow.Database.Sql()
var sql = uow.DatabaseContext.Sql()
.Select("nodeObjectType")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeId == id);
@@ -568,7 +569,7 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var sql = uow.Database.Sql()
var sql = uow.DatabaseContext.Sql()
.Select("nodeObjectType")
.From<NodeDto>()
.Where<NodeDto>(x => x.UniqueId == key);

View File

@@ -6,7 +6,6 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Web;
using LightInject;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPoco;
@@ -40,7 +39,7 @@ namespace Umbraco.Core.Sync
private bool _syncing;
private bool _released;
protected DatabaseServerMessengerOptions Options { get; private set; }
protected DatabaseServerMessengerOptions Options { get; }
public DatabaseServerMessenger(
IRuntimeState runtime, DatabaseContext dbContext, ILogger logger, ProfilingLogger proflog,

View File

@@ -223,6 +223,7 @@
<Compile Include="Constants-Icons.cs" />
<Compile Include="CoreRuntime.cs" />
<Compile Include="CoreRuntimeComponent.cs" />
<Compile Include="DatabaseBuilder.cs" />
<Compile Include="DI\Current.cs" />
<Compile Include="DatabaseContext.cs" />
<Compile Include="DataTableExtensions.cs" />
@@ -293,6 +294,7 @@
<Compile Include="Models\PublishedContent\NoopPublishedContentModelFactory.cs" />
<Compile Include="Models\PublishedContent\PropertyResult.cs" />
<Compile Include="Models\PublishedContent\PropertyResultType.cs" />
<Compile Include="Models\Rdbms\ContentNuDto.cs" />
<Compile Include="Persistence\IUmbracoDatabaseAccessor.cs" />
<Compile Include="Persistence\Mappers\IMapperCollection.cs" />
<Compile Include="Persistence\Mappers\MapperCollection.cs" />
@@ -451,8 +453,8 @@
<Compile Include="Persistence\Repositories\EntityContainerRepository.cs" />
<Compile Include="Persistence\Repositories\DomainRepository.cs" />
<Compile Include="Persistence\Repositories\ExternalLoginRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\DataTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\DocumentTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\DataTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\DocumentTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IDataTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IDocumentTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IEntityContainerRepository.cs" />
@@ -470,7 +472,7 @@
<Compile Include="Persistence\Repositories\Interfaces\IXsltFileRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\ITaskRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\ITaskTypeRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\MediaTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\MediaTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\MigrationEntryRepository.cs" />
<Compile Include="Persistence\Repositories\PublicAccessRepository.cs" />
<Compile Include="Persistence\Repositories\RedirectUrlRepository.cs" />
@@ -1017,7 +1019,7 @@
<Compile Include="Persistence\Repositories\Interfaces\IMemberTypeRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IRelationRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IRelationTypeRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IRepositoryQueryable.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IQueryRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IRepositoryVersionable.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IScriptRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IRepository.cs" />

View File

@@ -55,20 +55,19 @@ namespace Umbraco.Tests.Benchmarks
new [] { p },
logger,
new ThreadStaticUmbracoDatabaseAccessor(),
new QueryFactory(p, new MapperCollection(Enumerable.Empty<BaseMapper>())));
new MapperCollection(Enumerable.Empty<BaseMapper>()));
return f.GetDatabase();
}
private UmbracoDatabase GetSqlCeDatabase(string cstr, ILogger logger)
{
var p = new SqlCeSyntaxProvider();
var f = new DefaultDatabaseFactory(
cstr,
Constants.DatabaseProviders.SqlCe,
new[] { p },
new[] { new SqlCeSyntaxProvider() },
logger,
new ThreadStaticUmbracoDatabaseAccessor(),
new QueryFactory(p, new MapperCollection(Enumerable.Empty<BaseMapper>())));
new MapperCollection(Enumerable.Empty<BaseMapper>()));
return f.GetDatabase();
}

View File

@@ -28,7 +28,8 @@ namespace Umbraco.Tests.Migrations
_sqlSyntax = new SqlCeSyntaxProvider();
var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlServer);
_database = new UmbracoDatabase("cstr", _sqlSyntax, DatabaseType.SqlServer2008, dbProviderFactory, _logger);
var sqlContext = new SqlContext(_sqlSyntax, Mock.Of<IPocoDataFactory>(), DatabaseType.SqlServer2008);
_database = new UmbracoDatabase("cstr", sqlContext, dbProviderFactory, _logger);
}
[Test]

View File

@@ -30,7 +30,8 @@ namespace Umbraco.Tests.Migrations
_sqlSyntax = new SqlCeSyntaxProvider();
var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe);
_database = new UmbracoDatabase("cstr", _sqlSyntax, DatabaseType.SQLCe, dbProviderFactory, _logger);
var sqlContext = new SqlContext(_sqlSyntax, Mock.Of<IPocoDataFactory>(), DatabaseType.SQLCe);
_database = new UmbracoDatabase("cstr", sqlContext, dbProviderFactory, _logger);
_migrationContext = new MigrationContext(_database, _logger);
}

View File

@@ -24,10 +24,9 @@ namespace Umbraco.Tests.Migrations.Upgrades
public override UmbracoDatabase GetConfiguredDatabase()
{
var databaseType = DatabaseType.MySQL;
var sqlSyntax = new MySqlSyntaxProvider(Mock.Of<ILogger>());
var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.MySql);
return new UmbracoDatabase("Server = 169.254.120.3; Database = upgradetest; Uid = umbraco; Pwd = umbraco", sqlSyntax, databaseType, dbProviderFactory, Mock.Of<ILogger>());
var sqlContext = new SqlContext(new MySqlSyntaxProvider(Mock.Of<ILogger>()), Mock.Of<IPocoDataFactory>(), DatabaseType.MySQL);
return new UmbracoDatabase("Server = 169.254.120.3; Database = upgradetest; Uid = umbraco; Pwd = umbraco", sqlContext, dbProviderFactory, Mock.Of<ILogger>());
}
public override string GetDatabaseSpecificSqlScript()

View File

@@ -65,10 +65,9 @@ namespace Umbraco.Tests.Migrations.Upgrades
public override UmbracoDatabase GetConfiguredDatabase()
{
var databaseType = DatabaseType.SQLCe;
var sqlSyntax = new SqlCeSyntaxProvider();
var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe);
return new UmbracoDatabase("Datasource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;", sqlSyntax, databaseType, dbProviderFactory, Mock.Of<ILogger>());
var sqlContext = new SqlContext(new SqlCeSyntaxProvider(), Mock.Of<IPocoDataFactory>(), DatabaseType.SQLCe);
return new UmbracoDatabase("Datasource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;", sqlContext, dbProviderFactory, Mock.Of<ILogger>());
}
public override string GetDatabaseSpecificSqlScript()

View File

@@ -65,10 +65,9 @@ namespace Umbraco.Tests.Migrations.Upgrades
public override UmbracoDatabase GetConfiguredDatabase()
{
var databaseType = DatabaseType.SQLCe;
var sqlSyntax = new SqlCeSyntaxProvider();
var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe);
return new UmbracoDatabase("Datasource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;", sqlSyntax, databaseType, dbProviderFactory, Mock.Of<ILogger>());
var sqlContext = new SqlContext(new SqlCeSyntaxProvider(), Mock.Of<IPocoDataFactory>(), DatabaseType.SQLCe);
return new UmbracoDatabase("Datasource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;", sqlContext, dbProviderFactory, Mock.Of<ILogger>());
}
public override string GetDatabaseSpecificSqlScript()

View File

@@ -23,10 +23,9 @@ namespace Umbraco.Tests.Migrations.Upgrades
public override UmbracoDatabase GetConfiguredDatabase()
{
var databaseType = DatabaseType.SqlServer2008;
var sqlSyntax = new SqlServerSyntaxProvider(new Lazy<IDatabaseFactory>(() => null));
var dbProviderFactory = DbProviderFactories.GetFactory("System.Data.SqlClient");
return new UmbracoDatabase(@"server=.\SQLEXPRESS;database=EmptyForTest;user id=umbraco;password=umbraco", sqlSyntax, databaseType, dbProviderFactory, Mock.Of<ILogger>());
var sqlContext = new SqlContext(new SqlServerSyntaxProvider(new Lazy<IDatabaseFactory>(() => null)), Mock.Of<IPocoDataFactory>(), DatabaseType.SqlServer2008);
return new UmbracoDatabase(@"server=.\SQLEXPRESS;database=EmptyForTest;user id=umbraco;password=umbraco", sqlContext, dbProviderFactory, Mock.Of<ILogger>());
}
public override string GetDatabaseSpecificSqlScript()

View File

@@ -99,9 +99,9 @@ namespace Umbraco.Tests.Migrations.Upgrades
public UmbracoDatabase GetConfiguredDatabase()
{
var databaseType = DatabaseType.SQLCe;
var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe);
return new UmbracoDatabase("Datasource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;", new SqlCeSyntaxProvider(), databaseType, dbProviderFactory, Mock.Of<ILogger>());
var sqlContext = new SqlContext(new SqlCeSyntaxProvider(), Mock.Of<IPocoDataFactory>(), DatabaseType.SQLCe);
return new UmbracoDatabase("Datasource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;", sqlContext, dbProviderFactory, Mock.Of<ILogger>());
}
public string GetDatabaseSpecificSqlScript()

View File

@@ -26,7 +26,8 @@ namespace Umbraco.Tests.Migrations.Upgrades
_sqlSyntax = new SqlCeSyntaxProvider();
var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe);
_database = new UmbracoDatabase("cstr", _sqlSyntax, DatabaseType.SQLCe, dbProviderFactory, _logger);
var sqlContext = new SqlContext(_sqlSyntax, Mock.Of<IPocoDataFactory>(), DatabaseType.SQLCe);
_database = new UmbracoDatabase("cstr", sqlContext, dbProviderFactory, _logger);
}
[Test]

View File

@@ -38,10 +38,10 @@ namespace Umbraco.Tests.Persistence
_sqlCeSyntaxProvider = new SqlCeSyntaxProvider();
_sqlSyntaxProviders = new[] { (ISqlSyntaxProvider) _sqlCeSyntaxProvider };
_logger = Mock.Of<ILogger>();
var dbFactory = new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, _sqlSyntaxProviders, _logger, new TestUmbracoDatabaseAccessor(), Mock.Of<IQueryFactory>());
var dbFactory = new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, _sqlSyntaxProviders, _logger, new TestUmbracoDatabaseAccessor(), Mock.Of<IMapperCollection>());
_runtime = Mock.Of<IRuntimeState>();
_migrationEntryService = Mock.Of<IMigrationEntryService>();
_dbContext = new DatabaseContext(dbFactory, _logger, _runtime, _migrationEntryService);
_dbContext = new DatabaseContext(dbFactory);
}
[TearDown]
@@ -91,8 +91,8 @@ namespace Umbraco.Tests.Persistence
}
// re-create the database factory and database context with proper connection string
var dbFactory = new DefaultDatabaseFactory(connString, Constants.DbProviderNames.SqlCe, _sqlSyntaxProviders, _logger, new TestUmbracoDatabaseAccessor(), Mock.Of<IQueryFactory>());
_dbContext = new DatabaseContext(dbFactory, _logger, _runtime, _migrationEntryService);
var dbFactory = new DefaultDatabaseFactory(connString, Constants.DbProviderNames.SqlCe, _sqlSyntaxProviders, _logger, new TestUmbracoDatabaseAccessor(), Mock.Of<IMapperCollection>());
_dbContext = new DatabaseContext(dbFactory);
// create application context
//var appCtx = new ApplicationContext(
@@ -126,7 +126,7 @@ namespace Umbraco.Tests.Persistence
[TestCase("tcp:MyServer.database.windows.net,1433", "MyDatabase", "MyUser@MyServer", "MyPassword")]
public void Build_Azure_Connection_String_Regular(string server, string databaseName, string userName, string password)
{
var connectionString = DatabaseContext.GetAzureConnectionString(server, databaseName, userName, password);
var connectionString = DatabaseBuilder.GetAzureConnectionString(server, databaseName, userName, password);
Assert.AreEqual(connectionString, "Server=tcp:MyServer.database.windows.net,1433;Database=MyDatabase;User ID=MyUser@MyServer;Password=MyPassword");
}
@@ -136,7 +136,7 @@ namespace Umbraco.Tests.Persistence
[TestCase("tcp:kzeej5z8ty.ssmsawacluster4.windowsazure.mscds.com", "MyDatabase", "MyUser@kzeej5z8ty", "MyPassword")]
public void Build_Azure_Connection_String_CustomServer(string server, string databaseName, string userName, string password)
{
var connectionString = DatabaseContext.GetAzureConnectionString(server, databaseName, userName, password);
var connectionString = DatabaseBuilder.GetAzureConnectionString(server, databaseName, userName, password);
Assert.AreEqual(connectionString, "Server=tcp:kzeej5z8ty.ssmsawacluster4.windowsazure.mscds.com,1433;Database=MyDatabase;User ID=MyUser@kzeej5z8ty;Password=MyPassword");
}
}

View File

@@ -23,7 +23,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling
const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=x;password=umbraco";
const string providerName = Constants.DbProviderNames.SqlServer;
var sqlSyntax = new[] { new SqlServerSyntaxProvider(new Lazy<IDatabaseFactory>(() => null)) };
var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of<ILogger>(), new TestUmbracoDatabaseAccessor(), Mock.Of<IQueryFactory>());
var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of<ILogger>(), new TestUmbracoDatabaseAccessor(), Mock.Of<IMapperCollection>());
var database = factory.GetDatabase();
//Act
@@ -38,7 +38,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling
const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=umbraco;password=umbraco";
const string providerName = Constants.DbProviderNames.SqlServer;
var sqlSyntax = new[] { new SqlServerSyntaxProvider(new Lazy<IDatabaseFactory>(() => null)) };
var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of<ILogger>(), new TestUmbracoDatabaseAccessor(), Mock.Of<IQueryFactory>());
var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of<ILogger>(), new TestUmbracoDatabaseAccessor(), Mock.Of<IMapperCollection>());
var database = factory.GetDatabase();
//Act

View File

@@ -18,7 +18,7 @@ namespace Umbraco.Tests.Persistence.Repositories
var provider = TestObjects.GetDatabaseUnitOfWorkProvider(Logger);
using (var unitOfWork = provider.CreateUnitOfWork())
{
var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, QueryFactory);
var repo = new AuditRepository(unitOfWork, CacheHelper, Logger);
repo.AddOrUpdate(new AuditItem(-1, "This is a System audit trail", AuditType.System, 0));
unitOfWork.Complete();
}

View File

@@ -40,7 +40,7 @@ namespace Umbraco.Tests.Persistence.Repositories
{
TemplateRepository tr;
var ctRepository = CreateRepository(unitOfWork, out contentTypeRepository, out tr);
dtdRepository = new DataTypeDefinitionRepository(unitOfWork, CacheHelper, Logger, contentTypeRepository, QueryFactory);
dtdRepository = new DataTypeDefinitionRepository(unitOfWork, CacheHelper, Logger, contentTypeRepository);
return ctRepository;
}
@@ -52,10 +52,10 @@ namespace Umbraco.Tests.Persistence.Repositories
private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out TemplateRepository templateRepository)
{
templateRepository = new TemplateRepository(unitOfWork, CacheHelper, Logger, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>(), QueryFactory);
var tagRepository = new TagRepository(unitOfWork, CacheHelper, Logger, QueryFactory);
contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper, Logger, templateRepository, QueryFactory);
var repository = new ContentRepository(unitOfWork, CacheHelper, Logger, contentTypeRepository, templateRepository, tagRepository, Mock.Of<IContentSection>(), QueryFactory);
templateRepository = new TemplateRepository(unitOfWork, CacheHelper, Logger, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>());
var tagRepository = new TagRepository(unitOfWork, CacheHelper, Logger);
contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper, Logger, templateRepository);
var repository = new ContentRepository(unitOfWork, CacheHelper, Logger, contentTypeRepository, templateRepository, tagRepository, Mock.Of<IContentSection>());
return repository;
}

View File

@@ -36,29 +36,29 @@ namespace Umbraco.Tests.Persistence.Repositories
private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository)
{
var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>(), QueryFactory);
var tagRepository = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, QueryFactory);
contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, templateRepository, QueryFactory);
var repository = new ContentRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, contentTypeRepository, templateRepository, tagRepository, Mock.Of<IContentSection>(), QueryFactory);
var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>());
var tagRepository = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger);
contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, templateRepository);
var repository = new ContentRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, contentTypeRepository, templateRepository, tagRepository, Mock.Of<IContentSection>());
return repository;
}
private ContentTypeRepository CreateRepository(IDatabaseUnitOfWork unitOfWork)
{
var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>(), QueryFactory);
var contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, templateRepository, QueryFactory);
var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>());
var contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, templateRepository);
return contentTypeRepository;
}
private MediaTypeRepository CreateMediaTypeRepository(IDatabaseUnitOfWork unitOfWork)
{
var contentTypeRepository = new MediaTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, QueryFactory);
var contentTypeRepository = new MediaTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger);
return contentTypeRepository;
}
private EntityContainerRepository CreateContainerRepository(IDatabaseUnitOfWork unitOfWork, Guid containerEntityType)
{
return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, QueryFactory, containerEntityType);
return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, containerEntityType);
}
//TODO Add test to verify SetDefaultTemplates updates both AllowedTemplates and DefaultTemplate(id).
@@ -71,7 +71,7 @@ namespace Umbraco.Tests.Persistence.Repositories
var provider = TestObjects.GetDatabaseUnitOfWorkProvider(Logger);
using (var unitOfWork = provider.CreateUnitOfWork())
{
var templateRepo = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>(), QueryFactory);
var templateRepo = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>());
var repository = CreateRepository(unitOfWork);
var templates = new[]
{

Some files were not shown because too many files have changed in this diff Show More