diff --git a/src/Umbraco.Core/CallContextScope.cs b/src/Umbraco.Core/CallContextScope.cs new file mode 100644 index 0000000000..1eb4589899 --- /dev/null +++ b/src/Umbraco.Core/CallContextScope.cs @@ -0,0 +1,25 @@ +using System.Runtime.Remoting.Messaging; + +namespace Umbraco.Core +{ + /// + /// A place to get/retrieve data in a current context (i.e. http, thread, etc...) + /// + internal class CallContextScope : IScopeContext + { + public object GetData(string key) + { + return CallContext.GetData(key); + } + + public void SetData(string key, object data) + { + CallContext.SetData(key, data); + } + + public void ClearData(string key) + { + CallContext.FreeNamedDataSlot(key); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/DefaultScopeContextFactory.cs b/src/Umbraco.Core/DefaultScopeContextFactory.cs new file mode 100644 index 0000000000..1152ee6b41 --- /dev/null +++ b/src/Umbraco.Core/DefaultScopeContextFactory.cs @@ -0,0 +1,16 @@ +using System.Web; +using Umbraco.Core.Persistence; + +namespace Umbraco.Core +{ + /// + /// Default scope context factory + /// + internal class DefaultScopeContextFactory : IScopeContextFactory + { + public IScopeContext GetContext() + { + return HttpContext.Current == null ? (IScopeContext)new CallContextScope() : new HttpContextScope(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/DependencyInjection/RepositoryCompositionRoot.cs b/src/Umbraco.Core/DependencyInjection/RepositoryCompositionRoot.cs index d579eb7d57..58d22f4fee 100644 --- a/src/Umbraco.Core/DependencyInjection/RepositoryCompositionRoot.cs +++ b/src/Umbraco.Core/DependencyInjection/RepositoryCompositionRoot.cs @@ -23,6 +23,8 @@ namespace Umbraco.Core.DependencyInjection container.Register("SqlCeSyntaxProvider"); container.Register("SqlServerSyntaxProvider"); + container.RegisterSingleton(); + // register database factory // 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 diff --git a/src/Umbraco.Core/HttpContextScope.cs b/src/Umbraco.Core/HttpContextScope.cs new file mode 100644 index 0000000000..594e5be964 --- /dev/null +++ b/src/Umbraco.Core/HttpContextScope.cs @@ -0,0 +1,25 @@ +using System.Web; + +namespace Umbraco.Core +{ + /// + /// A place to get/retrieve data in a current context (i.e. http, thread, etc...) + /// + internal class HttpContextScope : IScopeContext + { + public object GetData(string key) + { + return HttpContext.Current.Items[key]; + } + + public void SetData(string key, object data) + { + HttpContext.Current.Items[key] = data; + } + + public void ClearData(string key) + { + HttpContext.Current.Items.Remove(key); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/IScopeContext.cs b/src/Umbraco.Core/IScopeContext.cs new file mode 100644 index 0000000000..8a1a916acc --- /dev/null +++ b/src/Umbraco.Core/IScopeContext.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Core +{ + /// + /// A place to get/retrieve data in a current context (i.e. http, thread, etc...) + /// + internal interface IScopeContext + { + object GetData(string key); + void SetData(string key, object data); + void ClearData(string key); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/IScopeContextFactory.cs b/src/Umbraco.Core/IScopeContextFactory.cs new file mode 100644 index 0000000000..e402f23608 --- /dev/null +++ b/src/Umbraco.Core/IScopeContextFactory.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core +{ + /// + /// Gets an IScopedContext + /// + internal interface IScopeContextFactory + { + IScopeContext GetContext(); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs index d51c1f97ae..fef60db1d7 100644 --- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Configuration; using System.Data.Common; using System.Linq; -using System.Web; +using System.Threading; using NPoco; using NPoco.FluentMappings; using Umbraco.Core.Configuration; @@ -26,11 +26,12 @@ namespace Umbraco.Core.Persistence /// internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory { + private readonly IScopeContextFactory _scopeContextFactory; private readonly ISqlSyntaxProvider[] _sqlSyntaxProviders; - private readonly ILogger _logger; - private bool _configured; + private readonly ILogger _logger; - private DatabaseFactory _databaseFactory; + private const string HttpItemKey = "Umbraco.Core.Persistence.DefaultDatabaseFactory"; + private DatabaseFactory _databaseFactory; private IPocoDataFactory _pocoDataFactory; private string _connectionString; private string _providerName; @@ -39,9 +40,9 @@ namespace Umbraco.Core.Persistence private ISqlSyntaxProvider _sqlSyntax; private RetryPolicy _connectionRetryPolicy; private RetryPolicy _commandRetryPolicy; + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); - // fixme - what needs to be private fields vs public properties? - public bool Configured => _configured; + public bool Configured { get; private set; } public ISqlSyntaxProvider SqlSyntax { get @@ -51,20 +52,16 @@ namespace Umbraco.Core.Persistence } } - // very important to have ThreadStatic, - // see: http://issues.umbraco.org/issue/U4-2172 - [ThreadStatic] - private static Lazy _nonHttpInstance; - /// /// Initializes a new instance of the with the default connection, and a logger. /// /// The collection of available sql syntax providers. /// A logger. + /// /// Used by LightInject. - public DefaultDatabaseFactory(IEnumerable sqlSyntaxProviders, ILogger logger) - : this(GlobalSettings.UmbracoConnectionName, sqlSyntaxProviders, logger) - { + public DefaultDatabaseFactory(IEnumerable sqlSyntaxProviders, ILogger logger, IScopeContextFactory scopeContextFactory) + : this(GlobalSettings.UmbracoConnectionName, sqlSyntaxProviders, logger, scopeContextFactory) + { if (Configured == false) DatabaseContext.GiveLegacyAChance(this, logger); } @@ -75,15 +72,20 @@ namespace Umbraco.Core.Persistence /// The name of the connection string in web.config. /// The collection of available sql syntax providers. /// A logger + /// /// Used by the other ctor and in tests. - public DefaultDatabaseFactory(string connectionStringName, IEnumerable sqlSyntaxProviders, ILogger logger) - { + public DefaultDatabaseFactory(string connectionStringName, IEnumerable sqlSyntaxProviders, ILogger logger, IScopeContextFactory scopeContextFactory) + { if (sqlSyntaxProviders == null) throw new ArgumentNullException(nameof(sqlSyntaxProviders)); if (logger == null) throw new ArgumentNullException(nameof(logger)); + if (scopeContextFactory == null) throw new ArgumentNullException(nameof(scopeContextFactory)); if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentException("Value cannot be null nor empty.", nameof(connectionStringName)); _sqlSyntaxProviders = sqlSyntaxProviders.ToArray(); _logger = logger; + _scopeContextFactory = scopeContextFactory; + + _logger.Debug("Created!"); var settings = ConfigurationManager.ConnectionStrings[connectionStringName]; if (settings == null) @@ -97,61 +99,73 @@ namespace Umbraco.Core.Persistence /// /// The database connection string. /// The name of the database provider. - /// The collection of available sql syntax providers. + /// The collection of available sql syntax providers. /// A logger. + /// /// Used in tests. - public DefaultDatabaseFactory(string connectionString, string providerName, IEnumerable sqlSyntaxProviders, ILogger logger) - { + public DefaultDatabaseFactory(string connectionString, string providerName, IEnumerable sqlSyntaxProviders, ILogger logger, IScopeContextFactory scopeContextFactory) + { if (sqlSyntaxProviders == null) throw new ArgumentNullException(nameof(sqlSyntaxProviders)); if (logger == null) throw new ArgumentNullException(nameof(logger)); + if (scopeContextFactory == null) throw new ArgumentNullException(nameof(scopeContextFactory)); _sqlSyntaxProviders = sqlSyntaxProviders.ToArray(); _logger = logger; + _scopeContextFactory = scopeContextFactory; + + _logger.Debug("Created!"); if (string.IsNullOrWhiteSpace(connectionString) || string.IsNullOrWhiteSpace(providerName)) return; // not configured Configure(connectionString, providerName); - } + } public void Configure(string connectionString, string providerName) { - if (_configured) throw new InvalidOperationException("Already configured."); + using (new WriteLock(_lock)) + { + _logger.Debug("Configuring!"); - Mandate.ParameterNotNullOrEmpty(connectionString, nameof(connectionString)); - Mandate.ParameterNotNullOrEmpty(providerName, nameof(providerName)); + if (Configured) throw new InvalidOperationException("Already configured."); - _connectionString = connectionString; - _providerName = providerName; + Mandate.ParameterNotNullOrEmpty(connectionString, nameof(connectionString)); + Mandate.ParameterNotNullOrEmpty(providerName, nameof(providerName)); - _connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(_connectionString); - _commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(_connectionString); + _connectionString = connectionString; + _providerName = providerName; - _dbProviderFactory = DbProviderFactories.GetFactory(_providerName); - if (_dbProviderFactory == null) - throw new Exception($"Can't find a provider factory for provider name \"{_providerName}\"."); - _databaseType = DatabaseType.Resolve(_dbProviderFactory.GetType().Name, _providerName); - if (_databaseType == null) - throw new Exception($"Can't find an NPoco database type for provider name \"{_providerName}\"."); + _connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(_connectionString); + _commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(_connectionString); - _sqlSyntax = GetSqlSyntaxProvider(_providerName); - if (_sqlSyntax == null) - throw new Exception($"Can't find a sql syntax provider for provider name \"{_providerName}\"."); + _dbProviderFactory = DbProviderFactories.GetFactory(_providerName); + if (_dbProviderFactory == null) + throw new Exception($"Can't find a provider factory for provider name \"{_providerName}\"."); + _databaseType = DatabaseType.Resolve(_dbProviderFactory.GetType().Name, _providerName); + if (_databaseType == null) + throw new Exception($"Can't find an NPoco database type for provider name \"{_providerName}\"."); - // ensure we have only 1 set of mappers, and 1 PocoDataFactory, for all database - // so that everything NPoco is properly cached for the lifetime of the application - var mappers = new MapperCollection { new PocoMapper() }; - var factory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init()); - _pocoDataFactory = factory; - var config = new FluentConfig(xmappers => factory); + _sqlSyntax = GetSqlSyntaxProvider(_providerName); + if (_sqlSyntax == null) + throw new Exception($"Can't find a sql syntax provider for provider name \"{_providerName}\"."); - // create the database factory - _databaseFactory = DatabaseFactory.Config(x => x - .UsingDatabase(CreateDatabaseInstance) // creating UmbracoDatabase instances - .WithFluentConfig(config)); // with proper configuration + // ensure we have only 1 set of mappers, and 1 PocoDataFactory, for all database + // so that everything NPoco is properly cached for the lifetime of the application + var mappers = new MapperCollection { new PocoMapper() }; + var factory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init()); + _pocoDataFactory = factory; + var config = new FluentConfig(xmappers => factory); - _nonHttpInstance = new Lazy(() => (UmbracoDatabase) _databaseFactory.GetDatabase()); - _configured = true; + // create the database factory + _databaseFactory = DatabaseFactory.Config(x => x + .UsingDatabase(CreateDatabaseInstance) // creating UmbracoDatabase instances + .WithFluentConfig(config)); // with proper configuration + + if (_databaseFactory == null) throw new NullReferenceException("The call to DatabaseFactory.Config yielded a null DatabaseFactory instance"); + + _logger.Debug("Created _nonHttpInstance"); + Configured = true; + } } // gets the sql syntax provider that corresponds, from attribute @@ -174,19 +188,22 @@ namespace Umbraco.Core.Persistence /// Gets a value indicating whether it is possible to connect to the database. /// /// - public bool CanConnect => _configured && DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName); + public bool CanConnect => Configured && DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName); private void EnsureConfigured() { - if (_configured == false) - throw new InvalidOperationException("Not configured."); + using (new ReadLock(_lock)) + { + if (Configured == false) + throw new InvalidOperationException("Not configured."); + } } // 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, _sqlSyntax, _databaseType, _dbProviderFactory, _logger, _connectionRetryPolicy, _commandRetryPolicy); + } /// /// Gets (creates or retrieves) the "ambient" database connection. @@ -196,43 +213,29 @@ namespace Umbraco.Core.Persistence { EnsureConfigured(); - // no http context, create the thread-static singleton object - if (HttpContext.Current == null) - { - return _nonHttpInstance.Value; - } + var scope = _scopeContextFactory.GetContext(); - // we have an http context, so only create one per request - var db = HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] as UmbracoDatabase; - if (db == null) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] = db = (UmbracoDatabase) _databaseFactory.GetDatabase(); - return db; - } - - protected override void DisposeResources() - { + // check if it's in scope + var db = scope.GetData(HttpItemKey) as UmbracoDatabase; + if (db != null) return db; + db = (UmbracoDatabase) _databaseFactory.GetDatabase(); + scope.SetData(HttpItemKey, db); + return db; + } + + protected override void DisposeResources() + { // this is weird, because _nonHttpInstance is thread-static, so we would need // to dispose the factory in each thread where a database has been used - else // it only disposes the current thread's database instance. // // besides, we don't really want to dispose the factory, which is a singleton... - - UmbracoDatabase db = null; - - if (HttpContext.Current == null) - { - if (_nonHttpInstance.IsValueCreated) - { - db = _nonHttpInstance.Value; - _nonHttpInstance = null; - } - } - else - { - db = HttpContext.Current.Items[typeof(DefaultDatabaseFactory)] as UmbracoDatabase; - HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] = null; - } - - db?.Dispose(); - } - } + + var scope = _scopeContextFactory.GetContext(); + var db = scope.GetData(HttpItemKey) as UmbracoDatabase; + scope.ClearData(HttpItemKey); + db?.Dispose(); + Configured = false; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWorkProvider.cs index 156d1df224..dcea35cedb 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWorkProvider.cs @@ -38,7 +38,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// internal NPocoUnitOfWorkProvider(ILogger logger) { - _databaseFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, GetDefaultSqlSyntaxProviders(logger), logger); + _databaseFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, GetDefaultSqlSyntaxProviders(logger), logger, new DefaultScopeContextFactory()); // careful, _repositoryFactory remains null! } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index e5f5e0e7c6..aeacb4c613 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -377,10 +377,15 @@ + + Component + + + diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs index 3d1e4a268e..b1545f0c40 100644 --- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs +++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Persistence _sqlCeSyntaxProvider = new SqlCeSyntaxProvider(); _sqlSyntaxProviders = new[] { (ISqlSyntaxProvider) _sqlCeSyntaxProvider }; _logger = Mock.Of(); - var dbFactory = new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, _sqlSyntaxProviders, _logger); + var dbFactory = new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, _sqlSyntaxProviders, _logger, new TestScopeContextFactory()); _dbContext = new DatabaseContext(dbFactory, _logger); } @@ -79,7 +79,7 @@ namespace Umbraco.Tests.Persistence engine.CreateDatabase(); // re-create the database factory and database context with proper connection string - var dbFactory = new DefaultDatabaseFactory(engine.LocalConnectionString, Constants.DbProviderNames.SqlCe, _sqlSyntaxProviders, _logger); + var dbFactory = new DefaultDatabaseFactory(engine.LocalConnectionString, Constants.DbProviderNames.SqlCe, _sqlSyntaxProviders, _logger, new TestScopeContextFactory()); _dbContext = new DatabaseContext(dbFactory, _logger); // create application context diff --git a/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs b/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs index 1d5127db3d..5df66b1f54 100644 --- a/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs +++ b/src/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs @@ -6,6 +6,7 @@ using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Persistence.FaultHandling { @@ -19,7 +20,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(() => null)) }; - var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of()); + var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of(), new TestScopeContextFactory()); var database = factory.GetDatabase(); //Act @@ -34,7 +35,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(() => null)) }; - var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of()); + var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of(), new TestScopeContextFactory()); var database = factory.GetDatabase(); //Act diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index e3931c7068..9e2b21e3b6 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1333,7 +1333,11 @@ namespace Umbraco.Tests.Services [Test] public void Can_Save_Lazy_Content() { - var databaseFactory = new DefaultDatabaseFactory(Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName, TestObjects.GetDefaultSqlSyntaxProviders(Logger), Logger); + var databaseFactory = new DefaultDatabaseFactory( + Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName, + TestObjects.GetDefaultSqlSyntaxProviders(Logger), + Logger, + new TestScopeContextFactory()); var repositoryFactory = MockRepositoryFactory(); var provider = new NPocoUnitOfWorkProvider(databaseFactory, repositoryFactory); var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 671c6e3f74..02a0888458 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -107,7 +107,7 @@ namespace Umbraco.Tests.TestHelpers // use a mock factory; otherwise use a real factory. var databaseFactory = DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture ? TestObjects.GetIDatabaseFactoryMock() - : new DefaultDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), sqlSyntaxProviders, Logger); + : new DefaultDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), sqlSyntaxProviders, Logger, new TestScopeContextFactory()); // so, using the above code to create a mock IDatabaseFactory if we don't have a real database // but, that will NOT prevent _appContext from NOT being configured, because it cannot connect diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index bf0db73e6d..f08458d671 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -219,7 +219,10 @@ namespace Umbraco.Tests.TestHelpers var evtMsgs = new TransientMessagesFactory(); ApplicationContext.Current = new ApplicationContext( //assign the db context - new DatabaseContext(new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, TestObjects.GetDefaultSqlSyntaxProviders(Logger), Logger), Logger), + new DatabaseContext(new DefaultDatabaseFactory( + Core.Configuration.GlobalSettings.UmbracoConnectionName, + TestObjects.GetDefaultSqlSyntaxProviders(Logger), + Logger, new TestScopeContextFactory()), Logger), //assign the service context TestObjects.GetServiceContext( Container.GetInstance(), diff --git a/src/Umbraco.Tests/TestHelpers/TestScopeContext.cs b/src/Umbraco.Tests/TestHelpers/TestScopeContext.cs new file mode 100644 index 0000000000..cadb3ff84f --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/TestScopeContext.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Umbraco.Core; + +namespace Umbraco.Tests.TestHelpers +{ + internal class TestScopeContext : IScopeContext + { + private readonly Dictionary _d = new Dictionary(); + + public object GetData(string key) + { + if (_d.ContainsKey(key)) return _d[key]; + return null; + } + + public void SetData(string key, object data) + { + _d[key] = data; + } + + public void ClearData(string key) + { + if (_d.ContainsKey(key)) + _d.Remove(key); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/TestScopeContextFactory.cs b/src/Umbraco.Tests/TestHelpers/TestScopeContextFactory.cs new file mode 100644 index 0000000000..ba16271526 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/TestScopeContextFactory.cs @@ -0,0 +1,20 @@ +using Umbraco.Core; + +namespace Umbraco.Tests.TestHelpers +{ + internal class TestScopeContextFactory : IScopeContextFactory + { + private readonly bool _transient = false; + private TestScopeContext _ctx; + + public TestScopeContextFactory(bool transient = false) + { + _transient = transient; + } + + public IScopeContext GetContext() + { + return _transient ? new TestScopeContext() : (_ctx ?? (_ctx = new TestScopeContext())); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index b876bddc05..8292be117f 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -187,6 +187,8 @@ + + diff --git a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml index 8142cf98f2..9481a6f932 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml @@ -64,7 +64,7 @@