From f0d1c38aa8dd8eeca652e1750d50b5e65a14b3cf Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 17 Dec 2019 15:03:14 +0100 Subject: [PATCH] AB3791 - Moved scoping to infrastructure, implemented our own CallContext --- .../Scoping/IScopeContext.cs | 4 +- src/Umbraco.Core/Umbraco.Core.csproj | 7 --- .../Events/QueuingEventDispatcher.cs | 18 ++++---- .../Scoping/CallContext.cs | 41 +++++++++++++++++ .../Scoping/IScopeAccessor.cs | 0 .../Scoping/Scope.cs | 29 ++++++++---- .../Scoping/ScopeContext.cs | 0 .../Scoping/ScopeContextualBase.cs | 0 .../Scoping/ScopeProvider.cs | 45 ++++++++++--------- .../Scoping/ScopeReference.cs | 0 .../Umbraco.Infrastructure.csproj | 18 +++----- .../Components/ComponentTests.cs | 4 +- .../Scoping/ScopeEventDispatcherTests.cs | 2 +- src/Umbraco.Tests/Scoping/ScopeTests.cs | 9 ++-- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 4 +- .../TestHelpers/TestWithDatabaseBase.cs | 1 - src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 2 + 17 files changed, 119 insertions(+), 65 deletions(-) rename src/{Umbraco.Core => Umbraco.Infrastructure}/Events/QueuingEventDispatcher.cs (71%) create mode 100644 src/Umbraco.Infrastructure/Scoping/CallContext.cs rename src/{Umbraco.Core => Umbraco.Infrastructure}/Scoping/IScopeAccessor.cs (100%) rename src/{Umbraco.Core => Umbraco.Infrastructure}/Scoping/Scope.cs (92%) rename src/{Umbraco.Core => Umbraco.Infrastructure}/Scoping/ScopeContext.cs (100%) rename src/{Umbraco.Core => Umbraco.Infrastructure}/Scoping/ScopeContextualBase.cs (100%) rename src/{Umbraco.Core => Umbraco.Infrastructure}/Scoping/ScopeProvider.cs (91%) rename src/{Umbraco.Core => Umbraco.Infrastructure}/Scoping/ScopeReference.cs (100%) diff --git a/src/Umbraco.Abstractions/Scoping/IScopeContext.cs b/src/Umbraco.Abstractions/Scoping/IScopeContext.cs index 0a267e67e2..719cc5f0ad 100644 --- a/src/Umbraco.Abstractions/Scoping/IScopeContext.cs +++ b/src/Umbraco.Abstractions/Scoping/IScopeContext.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Scoping /// A scope context can enlist objects that will be attached to the scope, and available /// for the duration of the scope. In addition, it can enlist actions, that will run when the /// scope is exiting, and after the database transaction has been committed. - public interface IScopeContext + public interface IScopeContext : IInstanceIdentifiable { /// /// Enlists an action. @@ -46,5 +46,7 @@ namespace Umbraco.Core.Scoping /// The object unique identifier. /// The enlisted object, if any, else the default value. T GetEnlisted(string key); + + void ScopeExit(bool completed); } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 22b5230e54..7adc7fd0ee 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -155,7 +155,6 @@ - @@ -215,12 +214,6 @@ - - - - - - diff --git a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs b/src/Umbraco.Infrastructure/Events/QueuingEventDispatcher.cs similarity index 71% rename from src/Umbraco.Core/Events/QueuingEventDispatcher.cs rename to src/Umbraco.Infrastructure/Events/QueuingEventDispatcher.cs index 88b527e753..6260aaa176 100644 --- a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs +++ b/src/Umbraco.Infrastructure/Events/QueuingEventDispatcher.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Composing; +using Umbraco.Composing; +using Umbraco.Core.Composing; using Umbraco.Core.IO; namespace Umbraco.Core.Events @@ -7,11 +8,14 @@ namespace Umbraco.Core.Events /// An IEventDispatcher that queues events, and raise them when the scope /// exits and has been completed. /// - internal class QueuingEventDispatcher : QueuingEventDispatcherBase + public class QueuingEventDispatcher : QueuingEventDispatcherBase { - public QueuingEventDispatcher() + private readonly IMediaFileSystem _mediaFileSystem; + public QueuingEventDispatcher(IMediaFileSystem mediaFileSystem) : base(true) - { } + { + _mediaFileSystem = mediaFileSystem; + } protected override void ScopeExitCompleted() { @@ -28,13 +32,11 @@ namespace Umbraco.Core.Events // but then where should it be (without making things too complicated)? var delete = e.Args as IDeletingMediaFilesEventArgs; if (delete != null && delete.MediaFilesToDelete.Count > 0) - MediaFileSystem.DeleteMediaFiles(delete.MediaFilesToDelete); + _mediaFileSystem.DeleteMediaFiles(delete.MediaFilesToDelete); } } - private IMediaFileSystem _mediaFileSystem; - // TODO: inject - private IMediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.MediaFileSystem); + } } diff --git a/src/Umbraco.Infrastructure/Scoping/CallContext.cs b/src/Umbraco.Infrastructure/Scoping/CallContext.cs new file mode 100644 index 0000000000..7b256434cd --- /dev/null +++ b/src/Umbraco.Infrastructure/Scoping/CallContext.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Umbraco.Core.Scoping +{ + /// + /// Provides a way to set contextual data that flows with the call and + /// async context of a test or invocation. + /// + public static class CallContext + { + private static readonly ConcurrentDictionary _state = new ConcurrentDictionary(); + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item in the call context. + /// The object to store in the call context. + public static void SetData(string name, Guid? data) + { + _state[name + Thread.CurrentThread.ManagedThreadId] = data; + } + + + /// + /// Retrieves an object with the specified name from the . + /// + /// The name of the item in the call context. + /// The object in the call context associated with the specified name, or if not found. + public static Guid? GetData(string name) + { + return _state.TryGetValue(name + Thread.CurrentThread.ManagedThreadId, out var data) ? data : null; + } + + public static bool RemoveData(string name) + { + return _state.TryRemove(name+ Thread.CurrentThread.ManagedThreadId, out _); + } + } +} diff --git a/src/Umbraco.Core/Scoping/IScopeAccessor.cs b/src/Umbraco.Infrastructure/Scoping/IScopeAccessor.cs similarity index 100% rename from src/Umbraco.Core/Scoping/IScopeAccessor.cs rename to src/Umbraco.Infrastructure/Scoping/IScopeAccessor.cs diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs similarity index 92% rename from src/Umbraco.Core/Scoping/Scope.cs rename to src/Umbraco.Infrastructure/Scoping/Scope.cs index 3eabfbca9b..8f7a0bf958 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -2,6 +2,7 @@ using System.Data; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -16,6 +17,8 @@ namespace Umbraco.Core.Scoping internal class Scope : IScope { private readonly ScopeProvider _scopeProvider; + private readonly ICoreDebug _coreDebug; + private readonly IMediaFileSystem _mediaFileSystem; private readonly ILogger _logger; private readonly ITypeFinder _typeFinder; @@ -36,7 +39,9 @@ namespace Umbraco.Core.Scoping // initializes a new scope private Scope(ScopeProvider scopeProvider, - ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, Scope parent, ScopeContext scopeContext, bool detachable, + ICoreDebug coreDebug, + IMediaFileSystem mediaFileSystem, + ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, Scope parent, IScopeContext scopeContext, bool detachable, IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, @@ -45,6 +50,8 @@ namespace Umbraco.Core.Scoping bool autoComplete = false) { _scopeProvider = scopeProvider; + _coreDebug = coreDebug; + _mediaFileSystem = mediaFileSystem; _logger = logger; _typeFinder = typeFinder; @@ -111,18 +118,22 @@ namespace Umbraco.Core.Scoping // initializes a new scope public Scope(ScopeProvider scopeProvider, - ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, bool detachable, ScopeContext scopeContext, + ICoreDebug coreDebug, + IMediaFileSystem mediaFileSystem, + ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, bool detachable, IScopeContext scopeContext, IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, logger, typeFinder, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebug, mediaFileSystem, logger, typeFinder, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } // initializes a new scope in a nested scopes chain, with its parent public Scope(ScopeProvider scopeProvider, + ICoreDebug coreDebug, + IMediaFileSystem mediaFileSystem, ILogger logger, ITypeFinder typeFinder, FileSystems fileSystems, Scope parent, IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, @@ -130,7 +141,7 @@ namespace Umbraco.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, logger, typeFinder, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebug, mediaFileSystem, logger, typeFinder, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } public Guid InstanceId { get; } = Guid.NewGuid(); @@ -194,10 +205,10 @@ namespace Umbraco.Core.Scoping public Scope OrigScope { get; set; } // the original context (when attaching a detachable scope) - public ScopeContext OrigContext { get; set; } + public IScopeContext OrigContext { get; set; } // the context (for attaching & detaching only) - public ScopeContext Context { get; } + public IScopeContext Context { get; } public IsolationLevel IsolationLevel { @@ -289,7 +300,7 @@ namespace Umbraco.Core.Scoping { EnsureNotDisposed(); if (ParentScope != null) return ParentScope.Events; - return _eventDispatcher ?? (_eventDispatcher = new QueuingEventDispatcher()); + return _eventDispatcher ?? (_eventDispatcher = new QueuingEventDispatcher(_mediaFileSystem)); } } @@ -484,8 +495,8 @@ namespace Umbraco.Core.Scoping // caching config // true if Umbraco.CoreDebug.LogUncompletedScope appSetting is set to "true" - private static bool LogUncompletedScopes => (_logUncompletedScopes - ?? (_logUncompletedScopes = Current.Configs.CoreDebug().LogUncompletedScopes)).Value; + private bool LogUncompletedScopes => (_logUncompletedScopes + ?? (_logUncompletedScopes = _coreDebug.LogUncompletedScopes)).Value; /// public void ReadLock(params int[] lockIds) => Database.SqlContext.SqlSyntax.ReadLock(Database, lockIds); diff --git a/src/Umbraco.Core/Scoping/ScopeContext.cs b/src/Umbraco.Infrastructure/Scoping/ScopeContext.cs similarity index 100% rename from src/Umbraco.Core/Scoping/ScopeContext.cs rename to src/Umbraco.Infrastructure/Scoping/ScopeContext.cs diff --git a/src/Umbraco.Core/Scoping/ScopeContextualBase.cs b/src/Umbraco.Infrastructure/Scoping/ScopeContextualBase.cs similarity index 100% rename from src/Umbraco.Core/Scoping/ScopeContextualBase.cs rename to src/Umbraco.Infrastructure/Scoping/ScopeContextualBase.cs diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs similarity index 91% rename from src/Umbraco.Core/Scoping/ScopeProvider.cs rename to src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index c8b5b05f59..ef3afb2dac 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -1,13 +1,15 @@ using System; using System.Collections.Generic; using System.Data; -using System.Runtime.Remoting.Messaging; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; +using Current = Umbraco.Composing.Current; + #if DEBUG_SCOPES using System.Linq; using System.Text; @@ -24,15 +26,18 @@ namespace Umbraco.Core.Scoping private readonly ITypeFinder _typeFinder; private readonly IRequestCache _requestCache; private readonly FileSystems _fileSystems; + private readonly ICoreDebug _coreDebug; + private readonly IMediaFileSystem _mediaFileSystem; - public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, ILogger logger, ITypeFinder typeFinder, IRequestCache requestCache) + public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, ICoreDebug coreDebug, IMediaFileSystem mediaFileSystem, ILogger logger, ITypeFinder typeFinder, IRequestCache requestCache) { DatabaseFactory = databaseFactory; _fileSystems = fileSystems; + _coreDebug = coreDebug; + _mediaFileSystem = mediaFileSystem; _logger = logger; _typeFinder = typeFinder; _requestCache = requestCache; - // take control of the FileSystems _fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems; @@ -105,12 +110,12 @@ namespace Umbraco.Core.Scoping private static T GetCallContextObject(string key) where T : class { - var objectKey = CallContext.LogicalGetData(key).AsGuid(); - if (objectKey == Guid.Empty) return null; + var objectKey = CallContext.GetData(key); + if (objectKey is null) return null; lock (StaticCallContextObjectsLock) { - if (StaticCallContextObjects.TryGetValue(objectKey, out object callContextObject)) + if (StaticCallContextObjects.TryGetValue(objectKey.Value, out object callContextObject)) { #if DEBUG_SCOPES Current.Logger.Debug("Got " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8)); @@ -120,7 +125,7 @@ namespace Umbraco.Core.Scoping } // hard to inject into a static method :( - Current.Logger.Warn("Missed {TypeName} Object {ObjectKey}", typeof(T).Name, objectKey.ToString("N").Substring(0, 8)); + Current.Logger.Warn("Missed {TypeName} Object {ObjectKey}", typeof(T).Name, objectKey.Value.ToString("N").Substring(0, 8)); #if DEBUG_SCOPES //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); #endif @@ -136,7 +141,7 @@ namespace Umbraco.Core.Scoping if (key == ScopeItemKey) { // first, null-register the existing value - var ambientKey = CallContext.LogicalGetData(ScopeItemKey).AsGuid(); + var ambientKey = CallContext.GetData(ScopeItemKey).AsGuid(); object o = null; lock (StaticCallContextObjectsLock) { @@ -152,16 +157,16 @@ namespace Umbraco.Core.Scoping #endif if (value == null) { - var objectKey = CallContext.LogicalGetData(key).AsGuid(); - CallContext.FreeNamedDataSlot(key); - if (objectKey == default) return; + var objectKey = CallContext.GetData(key); + CallContext.RemoveData(key); + if (objectKey is null) return; lock (StaticCallContextObjectsLock) { #if DEBUG_SCOPES Current.Logger.Debug("Remove Object " + objectKey.ToString("N").Substring(0, 8)); //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); #endif - StaticCallContextObjects.Remove(objectKey); + StaticCallContextObjects.Remove(objectKey.Value); } } else @@ -178,7 +183,7 @@ namespace Umbraco.Core.Scoping #endif StaticCallContextObjects.Add(objectKey, value); } - CallContext.LogicalSetData(key, objectKey); + CallContext.SetData(key, objectKey); } } @@ -228,13 +233,13 @@ namespace Umbraco.Core.Scoping internal const string ContextItemKey = "Umbraco.Core.Scoping.ScopeContext"; - public ScopeContext AmbientContext + public IScopeContext AmbientContext { get { // try http context, fallback onto call context - var value = GetHttpContextObject(ContextItemKey, false); - return value ?? GetCallContextObject(ContextItemKey); + var value = GetHttpContextObject(ContextItemKey, false); + return value ?? GetCallContextObject(ContextItemKey); } set { @@ -285,7 +290,7 @@ namespace Umbraco.Core.Scoping #endregion - public void SetAmbient(Scope scope, ScopeContext context = null) + public void SetAmbient(Scope scope, IScopeContext context = null) { // clear all SetHttpContextObject(ScopeItemKey, null, false); @@ -319,7 +324,7 @@ namespace Umbraco.Core.Scoping IEventDispatcher eventDispatcher = null, bool? scopeFileSystems = null) { - return new Scope(this, _logger, _typeFinder, _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); + return new Scope(this, _coreDebug, _mediaFileSystem, _logger, _typeFinder, _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); } /// @@ -375,13 +380,13 @@ namespace Umbraco.Core.Scoping { var ambientContext = AmbientContext; var newContext = ambientContext == null ? new ScopeContext() : null; - var scope = new Scope(this, _logger, _typeFinder, _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var scope = new Scope(this, _coreDebug, _mediaFileSystem, _logger, _typeFinder, _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); // assign only if scope creation did not throw! SetAmbient(scope, newContext ?? ambientContext); return scope; } - var nested = new Scope(this, _logger, _typeFinder, _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var nested = new Scope(this, _coreDebug, _mediaFileSystem, _logger, _typeFinder, _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); SetAmbient(nested, AmbientContext); return nested; } diff --git a/src/Umbraco.Core/Scoping/ScopeReference.cs b/src/Umbraco.Infrastructure/Scoping/ScopeReference.cs similarity index 100% rename from src/Umbraco.Core/Scoping/ScopeReference.cs rename to src/Umbraco.Infrastructure/Scoping/ScopeReference.cs diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index f3fe26ca76..8e9baebbf0 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -28,18 +28,7 @@ <_UnmanagedRegistrationCache Remove="obj\Umbraco.Infrastructure.csproj.UnmanagedRegistration.cache" /> - - - - - - - - - - - - + @@ -65,8 +54,11 @@ - + + + + diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 880a1161fa..7166d17c3f 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -38,7 +38,9 @@ namespace Umbraco.Tests.Components var typeFinder = new TypeFinder(logger); var f = new UmbracoDatabaseFactory(logger, new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator, TestHelper.BulkSqlInsertProvider); var fs = new FileSystems(mock.Object, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); - var p = new ScopeProvider(f, fs, logger, typeFinder, NoAppCache.Instance); + var coreDebug = Mock.Of(); + var mediaFileSystem = Mock.Of(); + var p = new ScopeProvider(f, fs, coreDebug, mediaFileSystem, logger, typeFinder, NoAppCache.Instance); mock.Setup(x => x.GetInstance(typeof (ILogger))).Returns(logger); mock.Setup(x => x.GetInstance(typeof (IProfilingLogger))).Returns(new ProfilingLogger(Mock.Of(), Mock.Of())); diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index 0824501059..56d8e320e1 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -337,7 +337,7 @@ namespace Umbraco.Tests.Scoping { var counter = 0; IScope ambientScope = null; - ScopeContext ambientContext = null; + IScopeContext ambientContext = null; Guid value = Guid.Empty; var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()) as ScopeProvider; diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index b2a3b14800..d1f77d4ae0 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -8,7 +8,10 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using ScopeProviderStatic = Umbraco.Core.Scoping.ScopeProvider; +using CallContext = Umbraco.Core.Scoping.CallContext; + +//using CallContext = Umbraco.Core.Scoping.CallContext; + namespace Umbraco.Tests.Scoping { @@ -123,7 +126,7 @@ namespace Umbraco.Tests.Scoping Assert.AreSame(scope, ((Scope) nested).ParentScope); // it's moved over to call context - var callContextKey = CallContext.LogicalGetData(ScopeProviderStatic.ScopeItemKey).AsGuid(); + var callContextKey = CallContext.GetData(ScopeProvider.ScopeItemKey).AsGuid(); Assert.AreNotEqual(Guid.Empty, callContextKey); // only if Core.DEBUG_SCOPES are defined @@ -485,7 +488,7 @@ namespace Umbraco.Tests.Scoping bool? completed = null; IScope ambientScope = null; - ScopeContext ambientContext = null; + IScopeContext ambientContext = null; Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope()) diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index d5272f34b0..649c1e1f1e 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -248,7 +248,9 @@ namespace Umbraco.Tests.TestHelpers typeFinder = typeFinder ?? new TypeFinder(logger); fileSystems = fileSystems ?? new FileSystems(Current.Factory, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); - var scopeProvider = new ScopeProvider(databaseFactory, fileSystems, logger, typeFinder, NoAppCache.Instance); + var coreDebug = Current.Configs.CoreDebug(); + var mediaFileSystem = Mock.Of(); + var scopeProvider = new ScopeProvider(databaseFactory, fileSystems, coreDebug, mediaFileSystem, logger, typeFinder, NoAppCache.Instance); return scopeProvider; } diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 85ee03918c..ef8a0bc599 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -23,7 +23,6 @@ using Umbraco.Web.Security; using Umbraco.Web.Routing; using File = System.IO.File; using Umbraco.Core.Composing; -using Umbraco.Core.Hosting; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Scoping; using Umbraco.Tests.Testing; diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index c88fd353c1..8b6553ff80 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; +using System.Threading; using System.Xml.Linq; using Examine; using Moq; @@ -64,6 +65,7 @@ namespace Umbraco.Tests.Testing /// provides all the necessary environment, through DI. Yes, DI is bad in tests - unit tests. /// But it is OK in integration tests. /// + [Apartment(ApartmentState.STA)] public abstract class UmbracoTestBase { // this class