diff --git a/src/Umbraco.Core/Request/IRequestAccessor.cs b/src/Umbraco.Core/Request/IRequestAccessor.cs index 52d532de26..63a8de6b1e 100644 --- a/src/Umbraco.Core/Request/IRequestAccessor.cs +++ b/src/Umbraco.Core/Request/IRequestAccessor.cs @@ -1,8 +1,13 @@ +using System; +using Umbraco.Web.Routing; + namespace Umbraco.Core.Request { public interface IRequestAccessor { string GetRequestValue(string name); string GetQueryStringValue(string culture); + event EventHandler EndRequest; + event EventHandler RouteAttempt; } } diff --git a/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs index d63478ef96..d8ec82818d 100644 --- a/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs @@ -3,8 +3,10 @@ namespace Umbraco.Core.Sync /// /// An implementation that works by storing messages in the database. /// - public interface IBatchedDatabaseServerMessenger : IServerMessenger + public interface IBatchedDatabaseServerMessenger : IDatabaseServerMessenger { void FlushBatch(); + DatabaseServerMessengerOptions Options { get; } + void Startup(); } } diff --git a/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs new file mode 100644 index 0000000000..a49cfdd023 --- /dev/null +++ b/src/Umbraco.Core/Sync/IDatabaseServerMessenger.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Sync +{ + public interface IDatabaseServerMessenger: IServerMessenger + { + void Sync(); + } +} diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs b/src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs similarity index 100% rename from src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollection.cs rename to src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs diff --git a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs similarity index 87% rename from src/Umbraco.Web/BatchedDatabaseServerMessenger.cs rename to src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs index a62aad3e5d..78b9589a2e 100644 --- a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/BatchedDatabaseServerMessenger.cs @@ -1,21 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Sync; using Umbraco.Web.Routing; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Scoping; -using Umbraco.Web.Composing; -using System.ComponentModel; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; +using Umbraco.Core.Request; namespace Umbraco.Web { @@ -29,19 +25,30 @@ namespace Umbraco.Web { private readonly IUmbracoDatabaseFactory _databaseFactory; private readonly IRequestCache _requestCache; + private readonly IRequestAccessor _requestAccessor; public BatchedDatabaseServerMessenger( - IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, DatabaseServerMessengerOptions options, IHostingEnvironment hostingEnvironment, CacheRefresherCollection cacheRefreshers, IRequestCache requestCache) + IRuntimeState runtime, + IUmbracoDatabaseFactory databaseFactory, + IScopeProvider scopeProvider, + ISqlContext sqlContext, + IProfilingLogger proflog, + DatabaseServerMessengerOptions options, + IHostingEnvironment hostingEnvironment, + CacheRefresherCollection cacheRefreshers, + IRequestCache requestCache, + IRequestAccessor requestAccessor) : base(runtime, scopeProvider, sqlContext, proflog, true, options, hostingEnvironment, cacheRefreshers) { _databaseFactory = databaseFactory; _requestCache = requestCache; + _requestAccessor = requestAccessor; } // invoked by DatabaseServerRegistrarAndMessengerComponent - internal void Startup() + public void Startup() { - UmbracoModule.EndRequest += UmbracoModule_EndRequest; + _requestAccessor.EndRequest += UmbracoModule_EndRequest; if (_databaseFactory.CanConnect == false) { @@ -104,7 +111,7 @@ namespace Umbraco.Web protected ICollection GetBatch(bool create) { - var key = typeof (BatchedDatabaseServerMessenger).Name; + var key = nameof(BatchedDatabaseServerMessenger); if (!_requestCache.IsAvailable) return null; diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs similarity index 91% rename from src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs rename to src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index 1fa455ed8a..2a24e6f318 100644 --- a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -4,6 +4,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; +using Umbraco.Core.Request; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Sync; @@ -82,7 +83,7 @@ namespace Umbraco.Web.Compose { private object _locker = new object(); private readonly DatabaseServerRegistrar _registrar; - private readonly BatchedDatabaseServerMessenger _messenger; + private readonly IBatchedDatabaseServerMessenger _messenger; private readonly IRuntimeState _runtime; private readonly ILogger _logger; private readonly IServerRegistrationService _registrationService; @@ -91,13 +92,23 @@ namespace Umbraco.Web.Compose private bool _started; private IBackgroundTask[] _tasks; private IndexRebuilder _indexRebuilder; + private readonly IRequestAccessor _requestAccessor; - public DatabaseServerRegistrarAndMessengerComponent(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IHostingEnvironment hostingEnvironment, IndexRebuilder indexRebuilder) + public DatabaseServerRegistrarAndMessengerComponent( + IRuntimeState runtime, + IServerRegistrar serverRegistrar, + IServerMessenger serverMessenger, + IServerRegistrationService registrationService, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IndexRebuilder indexRebuilder, + IRequestAccessor requestAccessor) { _runtime = runtime; _logger = logger; _registrationService = registrationService; _indexRebuilder = indexRebuilder; + _requestAccessor = requestAccessor; // create task runner for DatabaseServerRegistrar _registrar = serverRegistrar as DatabaseServerRegistrar; @@ -108,7 +119,7 @@ namespace Umbraco.Web.Compose } // create task runner for BatchedDatabaseServerMessenger - _messenger = serverMessenger as BatchedDatabaseServerMessenger; + _messenger = serverMessenger as IBatchedDatabaseServerMessenger; if (_messenger != null) { _processTaskRunner = new BackgroundTaskRunner("ServerInstProcess", @@ -120,7 +131,7 @@ namespace Umbraco.Web.Compose { //We will start the whole process when a successful request is made if (_registrar != null || _messenger != null) - UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt += RegisterBackgroundTasksOnce; // must come last, as it references some _variables _messenger?.Startup(); @@ -137,7 +148,7 @@ namespace Umbraco.Web.Compose /// /// We require this because: /// - ApplicationContext.UmbracoApplicationUrl is initialized by UmbracoModule in BeginRequest - /// - RegisterServer is called on UmbracoModule.RouteAttempt which is triggered in ProcessRequest + /// - RegisterServer is called on _requestAccessor.RouteAttempt which is triggered in ProcessRequest /// we are safe, UmbracoApplicationUrl has been initialized /// private void RegisterBackgroundTasksOnce(object sender, RoutableAttemptEventArgs e) @@ -146,7 +157,7 @@ namespace Umbraco.Web.Compose { case EnsureRoutableOutcome.IsRoutable: case EnsureRoutableOutcome.NotDocumentRequest: - UmbracoModule.RouteAttempt -= RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt -= RegisterBackgroundTasksOnce; RegisterBackgroundTasks(); break; } @@ -196,11 +207,11 @@ namespace Umbraco.Web.Compose private class InstructionProcessTask : RecurringTaskBase { - private readonly DatabaseServerMessenger _messenger; + private readonly IDatabaseServerMessenger _messenger; private readonly ILogger _logger; public InstructionProcessTask(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - DatabaseServerMessenger messenger, ILogger logger) + IDatabaseServerMessenger messenger, ILogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _messenger = messenger; diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs similarity index 96% rename from src/Umbraco.Web/Scheduling/SchedulerComponent.cs rename to src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs index 061f42b9ba..2008737d33 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Request; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Sync; @@ -37,6 +38,7 @@ namespace Umbraco.Web.Scheduling private readonly IUmbracoSettingsSection _umbracoSettingsSection; private readonly IIOHelper _ioHelper; private readonly IServerMessenger _serverMessenger; + private readonly IRequestAccessor _requestAccessor; private BackgroundTaskRunner _keepAliveRunner; private BackgroundTaskRunner _publishingRunner; @@ -54,7 +56,7 @@ namespace Umbraco.Web.Scheduling HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger, IHostingEnvironment hostingEnvironment, IHealthChecks healthChecksConfig, - IUmbracoSettingsSection umbracoSettingsSection, IIOHelper ioHelper, IServerMessenger serverMessenger) + IUmbracoSettingsSection umbracoSettingsSection, IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor) { _runtime = runtime; _contentService = contentService; @@ -70,6 +72,7 @@ namespace Umbraco.Web.Scheduling _umbracoSettingsSection = umbracoSettingsSection ?? throw new ArgumentNullException(nameof(umbracoSettingsSection)); _ioHelper = ioHelper; _serverMessenger = serverMessenger; + _requestAccessor = requestAccessor; } public void Initialize() @@ -83,7 +86,7 @@ namespace Umbraco.Web.Scheduling _healthCheckRunner = new BackgroundTaskRunner("HealthCheckNotifier", _logger, _hostingEnvironment); // we will start the whole process when a successful request is made - UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; + _requestAccessor.RouteAttempt += RegisterBackgroundTasksOnce; } public void Terminate() @@ -97,7 +100,6 @@ namespace Umbraco.Web.Scheduling { case EnsureRoutableOutcome.IsRoutable: case EnsureRoutableOutcome.NotDocumentRequest: - UmbracoModule.RouteAttempt -= RegisterBackgroundTasksOnce; RegisterBackgroundTasks(); break; } diff --git a/src/Umbraco.Web/Scheduling/SchedulerComposer.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComposer.cs similarity index 100% rename from src/Umbraco.Web/Scheduling/SchedulerComposer.cs rename to src/Umbraco.Infrastructure/Scheduling/SchedulerComposer.cs diff --git a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs index 948304e4e4..5a46a37d43 100644 --- a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.Sync // but only processes instructions coming from remote servers, // thus ensuring that instructions run only once // - public class DatabaseServerMessenger : ServerMessengerBase + public class DatabaseServerMessenger : ServerMessengerBase, IDatabaseServerMessenger { private readonly IRuntimeState _runtime; private readonly ManualResetEvent _syncIdle; @@ -126,10 +126,6 @@ namespace Umbraco.Core.Sync const int weight = 10; - //TODO Why do we have interface if we expect to be exact type!!!? - // if (!(_runtime is RuntimeState runtime)) - // throw new NotSupportedException($"Unsupported IRuntimeState implementation {_runtime.GetType().FullName}, expecting {typeof(RuntimeState).FullName}."); - var registered = _runtime.MainDom.Register( () => { diff --git a/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs b/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs index 3b47cabbaf..b65254b181 100644 --- a/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs +++ b/src/Umbraco.Infrastructure/Sync/ServerMessengerBase.cs @@ -4,7 +4,6 @@ using System.Linq; using Newtonsoft.Json; using Umbraco.Composing; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; namespace Umbraco.Core.Sync diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index 77200be86e..3a504ad4e2 100644 --- a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Web; using Moq; @@ -20,14 +21,17 @@ namespace Umbraco.Tests.Cache public class DeepCloneAppCacheTests : RuntimeAppCacheTests { private DeepCloneAppCache _provider; + private ObjectCacheAppCache _memberCache; - protected override int GetTotalItemCount => HttpRuntime.Cache.Count; + protected override int GetTotalItemCount => _memberCache.MemoryCache.Count(); public override void Setup() { base.Setup(); var typeFinder = new TypeFinder(Mock.Of()); - _provider = new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache, typeFinder)); + _memberCache = new ObjectCacheAppCache(typeFinder); + + _provider = new DeepCloneAppCache(_memberCache); } internal override IAppCache AppCache => _provider; diff --git a/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs b/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs deleted file mode 100644 index 02986e2f78..0000000000 --- a/src/Umbraco.Tests/Cache/WebCachingAppCacheTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Diagnostics; -using System.Web; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; -using Umbraco.Web.Cache; - -namespace Umbraco.Tests.Cache -{ - [TestFixture] - public class WebCachingAppCacheTests : RuntimeAppCacheTests - { - private WebCachingAppCache _appCache; - - protected override int GetTotalItemCount => HttpRuntime.Cache.Count; - - public override void Setup() - { - base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); - _appCache = new WebCachingAppCache(HttpRuntime.Cache, typeFinder); - } - - internal override IAppCache AppCache => _appCache; - - internal override IAppPolicyCache AppPolicyCache => _appCache; - - [Test] - public void DoesNotCacheExceptions() - { - string value; - Assert.Throws(() => { value = (string)_appCache.Get("key", () => GetValue(1)); }); - Assert.Throws(() => { value = (string)_appCache.Get("key", () => GetValue(2)); }); - - // does not throw - value = (string)_appCache.Get("key", () => GetValue(3)); - Assert.AreEqual("succ3", value); - - // cache - value = (string)_appCache.Get("key", () => GetValue(4)); - Assert.AreEqual("succ3", value); - } - - private static string GetValue(int i) - { - Debug.Print("get" + i); - if (i < 3) - throw new Exception("fail"); - return "succ" + i; - } - } -} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9815c94728..56d36981b3 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -322,7 +322,6 @@ - diff --git a/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs b/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs index d2675b0dc1..72bde75dc8 100644 --- a/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs +++ b/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs @@ -1,4 +1,6 @@ +using System; using Umbraco.Core.Request; +using Umbraco.Web.Routing; namespace Umbraco.Web.AspNet { @@ -9,8 +11,13 @@ namespace Umbraco.Web.AspNet public AspNetRequestAccessor(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; + + UmbracoModule.EndRequest += OnEndRequest; + UmbracoModule.RouteAttempt += OnRouteAttempt; } + + public string GetRequestValue(string name) { return _httpContextAccessor.GetRequiredHttpContext().Request[name]; @@ -20,5 +27,17 @@ namespace Umbraco.Web.AspNet { return _httpContextAccessor.GetRequiredHttpContext().Request.QueryString[name]; } + + private void OnEndRequest(object sender, UmbracoRequestEventArgs args) + { + EndRequest?.Invoke(sender, args); + } + + private void OnRouteAttempt(object sender, RoutableAttemptEventArgs args) + { + RouteAttempt?.Invoke(sender, args); + } + public event EventHandler EndRequest; + public event EventHandler RouteAttempt; } } diff --git a/src/Umbraco.Web/Cache/WebCachingAppCache.cs b/src/Umbraco.Web/Cache/WebCachingAppCache.cs deleted file mode 100644 index 087d393d3c..0000000000 --- a/src/Umbraco.Web/Cache/WebCachingAppCache.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Web.Caching; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; - -namespace Umbraco.Web.Cache -{ - /// - /// Implements on top of a . - /// - /// The underlying cache is expected to be HttpRuntime.Cache. - internal class WebCachingAppCache : FastDictionaryAppCacheBase, IAppPolicyCache - { - // locker object that supports upgradeable read locking - // does not need to support recursion if we implement the cache correctly and ensure - // that methods cannot be reentrant, ie we do NOT create values while holding a lock. - private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); - - private readonly System.Web.Caching.Cache _cache; - - /// - /// Initializes a new instance of the class. - /// - public WebCachingAppCache(System.Web.Caching.Cache cache, ITypeFinder typeFinder) : base(typeFinder) - { - _cache = cache; - } - - /// - public override object Get(string key, Func factory) - { - return Get(key, factory, null, dependentFiles: null); - } - - /// - public object Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false, string[] dependentFiles = null) - { - CacheDependency dependency = null; - if (dependentFiles != null && dependentFiles.Any()) - { - dependency = new CacheDependency(dependentFiles); - } - return GetImpl(key, factory, timeout, isSliding, dependency); - } - - /// - public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false, string[] dependentFiles = null) - { - CacheDependency dependency = null; - if (dependentFiles != null && dependentFiles.Any()) - { - dependency = new CacheDependency(dependentFiles); - } - InsertImpl(key, factory, timeout, isSliding, dependency); - } - - #region Dictionary - - protected override IEnumerable GetDictionaryEntries() - { - const string prefix = CacheItemPrefix + "-"; - return _cache.Cast() - .Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix)); - } - - protected override void RemoveEntry(string key) - { - _cache.Remove(key); - } - - protected override object GetEntry(string key) - { - return _cache.Get(key); - } - - #endregion - - #region Lock - - protected override void EnterReadLock() - { - _locker.EnterReadLock(); - } - - protected override void EnterWriteLock() - { - _locker.EnterWriteLock(); - } - - protected override void ExitReadLock() - { - if (_locker.IsReadLockHeld) - _locker.ExitReadLock(); - } - - protected override void ExitWriteLock() - { - if (_locker.IsWriteLockHeld) - _locker.ExitWriteLock(); - } - - #endregion - - private object GetImpl(string key, Func factory, TimeSpan? timeout, bool isSliding = false, CacheDependency dependency = null) - { - key = GetCacheKey(key); - - // NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang, - // getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and - // nasty performance issues. - - // So.... we insert a Lazy in the cache while holding the global application lock, and then rely - // on the Lazy lock to ensure that getCacheItem runs once and everybody waits on it, while the global - // application lock has been released. - - // NOTE - // The Lazy value creation may produce a null value. - // Must make sure (for backward compatibility) that we pretend they are not in the cache. - // So if we find an entry in the cache that already has its value created and is null, - // pretend it was not there. If value is not already created, wait... and return null, that's - // what prior code did. - - // NOTE - // The Lazy value creation may throw. - - // So... the null value _will_ be in the cache but never returned - - Lazy result; - - // Fast! - // Only one thread can enter an UpgradeableReadLock at a time, but it does not prevent other - // threads to enter a ReadLock in the meantime -- only upgrading to WriteLock will prevent all - // reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of - // having to re-lock in case there's no value. Would need to benchmark to figure out whether - // it's worth it, though... - try - { - _locker.EnterReadLock(); - result = _cache.Get(key) as Lazy; // null if key not found - } - finally - { - if (_locker.IsReadLockHeld) - _locker.ExitReadLock(); - } - var value = result == null ? null : SafeLazy.GetSafeLazyValue(result); - if (value != null) return value; - - using (var lck = new UpgradeableReadLock(_locker)) - { - result = _cache.Get(key) as Lazy; // null if key not found - - // cannot create value within the lock, so if result.IsValueCreated is false, just - // do nothing here - means that if creation throws, a race condition could cause - // more than one thread to reach the return statement below and throw - accepted. - - if (result == null || SafeLazy.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null - { - result = SafeLazy.GetSafeLazy(factory); - var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); - var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); - - lck.UpgradeToWriteLock(); - //NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update! - _cache.Insert(key, result, dependency, absolute, sliding, CacheItemPriority.Normal, null); - } - } - - // using GetSafeLazy and GetSafeLazyValue ensures that we don't cache - // exceptions (but try again and again) and silently eat them - however at - // some point we have to report them - so need to re-throw here - - // this does not throw anymore - //return result.Value; - - value = result.Value; // will not throw (safe lazy) - if (value is SafeLazy.ExceptionHolder eh) eh.Exception.Throw(); // throw once! - return value; - } - - private void InsertImpl(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheDependency dependency = null) - { - // NOTE - here also we must insert a Lazy but we can evaluate it right now - // and make sure we don't store a null value. - - var result = SafeLazy.GetSafeLazy(getCacheItem); - var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache - if (value == null) return; // do not store null values (backward compat) - - cacheKey = GetCacheKey(cacheKey); - - var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); - var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); - - try - { - _locker.EnterWriteLock(); - //NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update! - _cache.Insert(cacheKey, result, dependency, absolute, sliding, CacheItemPriority.Normal, null); - } - finally - { - if (_locker.IsWriteLockHeld) - _locker.ExitWriteLock(); - } - } - } -} diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 796a3a0c4a..f8b121f60c 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -96,7 +96,7 @@ namespace Umbraco.Web } if (cacheByMember) { - var helper = Current.Factory.GetInstance(); + var helper = Current.MembershipHelper; var currentMember = helper.GetCurrentMember(); cacheKey.AppendFormat("m{0}-", currentMember?.Id ?? 0); } diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index 462748fae0..253013d8c4 100644 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -32,6 +32,7 @@ namespace Umbraco.Web.Macros private readonly IMemberUserKeyProvider _memberUserKeyProvider; private readonly ISessionManager _sessionManager; private readonly IRequestAccessor _requestAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; public MacroRenderer( @@ -45,7 +46,8 @@ namespace Umbraco.Web.Macros ICookieManager cookieManager, IMemberUserKeyProvider memberUserKeyProvider, ISessionManager sessionManager, - IRequestAccessor requestAccessor) + IRequestAccessor requestAccessor, + IHttpContextAccessor httpContextAccessor) { _plogger = plogger ?? throw new ArgumentNullException(nameof(plogger)); _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); @@ -58,6 +60,7 @@ namespace Umbraco.Web.Macros _memberUserKeyProvider = memberUserKeyProvider; _sessionManager = sessionManager; _requestAccessor = requestAccessor; + _httpContextAccessor = httpContextAccessor; } #region MacroContent cache diff --git a/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs index 8f77de67f2..21c965aa3d 100644 --- a/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs +++ b/src/Umbraco.Web/Models/Identity/BackOfficeIdentityUser.cs @@ -5,7 +5,6 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Identity; diff --git a/src/Umbraco.Web/Models/LoginStatusModel.cs b/src/Umbraco.Web/Models/LoginStatusModel.cs index e20c7f0a0b..4b699bab38 100644 --- a/src/Umbraco.Web/Models/LoginStatusModel.cs +++ b/src/Umbraco.Web/Models/LoginStatusModel.cs @@ -1,7 +1,5 @@ using System.ComponentModel.DataAnnotations; -using Umbraco.Core; -using Umbraco.Web.Security; -using Current = Umbraco.Web.Composing.Current; + namespace Umbraco.Web.Models { @@ -16,26 +14,9 @@ namespace Umbraco.Web.Models /// public static LoginStatusModel CreateModel() { - return new LoginStatusModel(false); + return new LoginStatusModel(); } - private LoginStatusModel(bool doLookup) - { - if (doLookup && Current.UmbracoContext != null) - { - var helper = Current.Factory.GetInstance(); - var model = helper.GetCurrentLoginStatus(); - if (model != null) - { - Name = model.Name; - Username = model.Username; - Email = model.Email; - IsLoggedIn = true; - } - } - } - - /// /// The name of the member /// diff --git a/src/Umbraco.Web/Models/Mapping/CommonTreeNodeMapper.cs b/src/Umbraco.Web/Models/Mapping/CommonTreeNodeMapper.cs index d1d0857abb..aed779683f 100644 --- a/src/Umbraco.Web/Models/Mapping/CommonTreeNodeMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/CommonTreeNodeMapper.cs @@ -1,18 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Mvc; -using Umbraco.Core; -using Umbraco.Core.Mapping; +using System.Web.Mvc; using Umbraco.Core.Models; -using Umbraco.Core.Models.ContentEditing; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Web.ContentApps; -using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile; namespace Umbraco.Web.Models.Mapping { diff --git a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs index 6e16dd2d71..5fc443f692 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberMapDefinition.cs @@ -1,5 +1,4 @@ -using System.Web.Mvc; -using Umbraco.Core; +using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; diff --git a/src/Umbraco.Web/Models/ProfileModel.cs b/src/Umbraco.Web/Models/ProfileModel.cs index 7b7b0ecb4a..0397f34849 100644 --- a/src/Umbraco.Web/Models/ProfileModel.cs +++ b/src/Umbraco.Web/Models/ProfileModel.cs @@ -3,9 +3,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Web.Mvc; -using Umbraco.Web.Security; -using Umbraco.Core; -using Umbraco.Core.Composing; using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Models @@ -28,7 +25,7 @@ namespace Umbraco.Web.Models MemberProperties = new List(); if (doLookup && Current.UmbracoContext != null) { - var helper = Current.Factory.GetInstance(); + var helper = Current.MembershipHelper; var model = helper.GetCurrentMemberProfileModel(); MemberProperties = model.MemberProperties; } diff --git a/src/Umbraco.Web/Models/RegisterModel.cs b/src/Umbraco.Web/Models/RegisterModel.cs index 41edea19de..1ee9307969 100644 --- a/src/Umbraco.Web/Models/RegisterModel.cs +++ b/src/Umbraco.Web/Models/RegisterModel.cs @@ -1,13 +1,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Web; using System.Web.Mvc; using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Web.Security; -using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Models { @@ -20,22 +15,16 @@ namespace Umbraco.Web.Models /// public static RegisterModel CreateModel() { - return new RegisterModel(false); + return new RegisterModel(); } - private RegisterModel(bool doLookup) + private RegisterModel() { MemberTypeAlias = Constants.Conventions.MemberTypes.DefaultAlias; UsernameIsEmail = true; MemberProperties = new List(); LoginOnSuccess = true; CreatePersistentLoginCookie = true; - if (doLookup && Current.UmbracoContext != null) - { - var helper = Current.Factory.GetInstance(); - var model = helper.CreateRegistrationModel(MemberTypeAlias); - MemberProperties = model.MemberProperties; - } } diff --git a/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs b/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs index 5f81ced3f0..a99e8c1df6 100644 --- a/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs +++ b/src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs @@ -48,9 +48,9 @@ namespace Umbraco.Web.Mvc } } - var helper = Current.Factory.GetInstance(); + var helper = Current.MembershipHelper; return helper.IsMemberAuthorized(AllowType.Split(','), AllowGroup.Split(','), members); - + } /// diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 345d847365..632cb5a6b6 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -71,7 +71,7 @@ namespace Umbraco.Web.Runtime composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); composition.Register(factory => factory.GetInstance(), Lifetime.Singleton); - composition.Register(); + composition.Register(Lifetime.Singleton); composition.Register(); composition.Register(); diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index 0fc35af2f2..7d268a7c42 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -22,7 +22,6 @@ namespace Umbraco.Web.Runtime /// On top of CoreRuntime, handles all of the web-related runtime aspects of Umbraco. public class WebRuntime : CoreRuntime { - private readonly UmbracoApplicationBase _umbracoApplication; private BuildManagerTypeFinder _typeFinder; /// @@ -42,8 +41,6 @@ namespace Umbraco.Web.Runtime IMainDom mainDom): base(configs, umbracoVersion, ioHelper, logger, profiler ,new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom) { - _umbracoApplication = umbracoApplication; - Profiler = GetWebProfiler(); } @@ -102,7 +99,7 @@ namespace Umbraco.Web.Runtime protected override AppCaches GetAppCaches() => new AppCaches( // we need to have the dep clone runtime cache provider to ensure // all entities are cached properly (cloned in and cloned out) - new DeepCloneAppCache(new WebCachingAppCache(HttpRuntime.Cache, TypeFinder)), + new DeepCloneAppCache(new ObjectCacheAppCache(TypeFinder)), // we need request based cache when running in web-based context new HttpRequestAppCache(() => HttpContext.Current?.Items, TypeFinder), new IsolatedCaches(type => diff --git a/src/Umbraco.Web/Security/EmailService.cs b/src/Umbraco.Web/Security/EmailService.cs index e6454544ab..aabd5a1ead 100644 --- a/src/Umbraco.Web/Security/EmailService.cs +++ b/src/Umbraco.Web/Security/EmailService.cs @@ -1,9 +1,6 @@ -using System; -using System.ComponentModel; -using System.Net.Mail; +using System.Net.Mail; using System.Threading.Tasks; using Microsoft.AspNet.Identity; -using Umbraco.Core.Configuration; namespace Umbraco.Core.Security { diff --git a/src/Umbraco.Web/Security/SessionIdValidator.cs b/src/Umbraco.Web/Security/SessionIdValidator.cs index 8f6782aac2..11fb28c197 100644 --- a/src/Umbraco.Web/Security/SessionIdValidator.cs +++ b/src/Umbraco.Web/Security/SessionIdValidator.cs @@ -10,7 +10,7 @@ using Microsoft.Owin.Security.Cookies; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.IO; -using Umbraco.Core.Security; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Security diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index ac08258ee2..8cf310d7ee 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -2,16 +2,15 @@ using System; using System.Globalization; using System.IO; using System.Linq; -using System.Web; using System.Web.Mvc; using System.Web.Routing; +using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.Routing; -using Umbraco.Core.Strings; namespace Umbraco.Web.Templates { @@ -46,10 +45,11 @@ namespace Umbraco.Web.Templates { if (writer == null) throw new ArgumentNullException(nameof(writer)); + var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); // instantiate a request and process // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. - var contentRequest = _publishedRouter.CreateRequest(_umbracoContextAccessor.UmbracoContext); + var contentRequest = _publishedRouter.CreateRequest(umbracoContext); var doc = contentRequest.UmbracoContext.Content.GetById(pageId); @@ -62,7 +62,7 @@ namespace Umbraco.Web.Templates //in some cases the UmbracoContext will not have a PublishedRequest assigned to it if we are not in the //execution of a front-end rendered page. In this case set the culture to the default. //set the culture to the same as is currently rendering - if (_umbracoContextAccessor.UmbracoContext.PublishedRequest == null) + if (umbracoContext.PublishedRequest == null) { var defaultLanguage = _languageService.GetAllLanguages().FirstOrDefault(); contentRequest.Culture = defaultLanguage == null @@ -71,7 +71,7 @@ namespace Umbraco.Web.Templates } else { - contentRequest.Culture = _umbracoContextAccessor.UmbracoContext.PublishedRequest.Culture; + contentRequest.Culture = umbracoContext.PublishedRequest.Culture; } //set the doc that was found by id diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index cd0c4152a0..fad79b0296 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -139,7 +139,6 @@ - @@ -192,8 +191,6 @@ - - @@ -307,7 +304,6 @@ - @@ -344,7 +340,6 @@ - @@ -527,7 +522,6 @@ - diff --git a/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs index bc1e9a4318..6d4607cfba 100644 --- a/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs +++ b/src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs @@ -46,7 +46,7 @@ namespace Umbraco.Web.WebApi } } - var helper = Current.Factory.GetInstance(); + var helper = Current.MembershipHelper; return helper.IsMemberAuthorized(AllowType.Split(','), AllowGroup.Split(','), members); }