diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 83bd3c5e4f..20a474f71d 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Reflection; +using System.Threading; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Composing; @@ -29,6 +30,13 @@ namespace Umbraco.Core.IO private ShadowWrapper _masterPagesFileSystem; private ShadowWrapper _mvcViewsFileSystem; + // well-known file systems lazy initialization + private object _wkfsLock = new object(); + private bool _wkfsInitialized; + private object _wkfsObject; + + private MediaFileSystem _mediaFileSystem; + #region Constructor // DI wants a public ctor @@ -37,7 +45,6 @@ namespace Umbraco.Core.IO { _config = (FileSystemProvidersSection)ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders"); _logger = logger; - CreateWellKnownFileSystems(); } // for tests only, totally unsafe @@ -46,25 +53,95 @@ namespace Umbraco.Core.IO _wrappers.Clear(); _providerLookup.Clear(); _filesystems.Clear(); - CreateWellKnownFileSystems(); + Volatile.Write(ref _wkfsInitialized, false); } - internal Func IsScoped { get; set; } + internal Func IsScoped { get; set; } = () => false; #endregion #region Well-Known FileSystems - public IFileSystem MacroPartialsFileSystem => _macroPartialFileSystem; - public IFileSystem PartialViewsFileSystem => _partialViewsFileSystem; - public IFileSystem StylesheetsFileSystem => _stylesheetsFileSystem; - public IFileSystem ScriptsFileSystem => _scriptsFileSystem; - public IFileSystem XsltFileSystem => _xsltFileSystem; - public IFileSystem MasterPagesFileSystem => _masterPagesFileSystem; // fixme - see 7.6?! - public IFileSystem MvcViewsFileSystem => _mvcViewsFileSystem; - public MediaFileSystem MediaFileSystem { get; private set; } + public IFileSystem MacroPartialsFileSystem + { + get + { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + return _macroPartialFileSystem; + } + } - private void CreateWellKnownFileSystems() + public IFileSystem PartialViewsFileSystem + { + get + { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + return _partialViewsFileSystem; + } + } + + public IFileSystem StylesheetsFileSystem + { + get + { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + return _stylesheetsFileSystem; + } + } + + public IFileSystem ScriptsFileSystem + { + get + { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + return _scriptsFileSystem; + } + } + + public IFileSystem XsltFileSystem + { + get + { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + return _xsltFileSystem; + } + } + + public IFileSystem MasterPagesFileSystem + { + get + { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + return _masterPagesFileSystem;// fixme - see 7.6?! + } + } + + public IFileSystem MvcViewsFileSystem + { + get + { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + return _mvcViewsFileSystem; + } + } + + public MediaFileSystem MediaFileSystem + { + get + { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + return _mediaFileSystem; + } + } + + private void EnsureWellKnownFileSystems() + { + LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems); + } + + // need to return something to LazyInitializer.EnsureInitialized + // but it does not really matter what we return - here, null + private object CreateWellKnownFileSystems() { var macroPartialFileSystem = new PhysicalFileSystem(SystemDirectories.MacroPartials); var partialViewsFileSystem = new PhysicalFileSystem(SystemDirectories.PartialViews); @@ -74,16 +151,18 @@ namespace Umbraco.Core.IO var masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages); var mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews); - _macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", IsScoped); - _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", IsScoped); - _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", IsScoped); - _scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", IsScoped); - _xsltFileSystem = new ShadowWrapper(xsltFileSystem, "xslt", IsScoped); - _masterPagesFileSystem = new ShadowWrapper(masterPagesFileSystem, "masterpages", IsScoped); - _mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", IsScoped); + _macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", () => IsScoped()); + _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", () => IsScoped()); + _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", () => IsScoped()); + _scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", () => IsScoped()); + _xsltFileSystem = new ShadowWrapper(xsltFileSystem, "xslt", () => IsScoped()); + _masterPagesFileSystem = new ShadowWrapper(masterPagesFileSystem, "masterpages", () => IsScoped()); + _mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", () => IsScoped()); // filesystems obtained from GetFileSystemProvider are already wrapped and do not need to be wrapped again - MediaFileSystem = GetFileSystemProvider(); + _mediaFileSystem = GetFileSystemProvider(); + + return null; } #endregion @@ -215,7 +294,7 @@ namespace Umbraco.Core.IO // so we are double-wrapping here // could be optimized by having FileSystemWrapper inherit from ShadowWrapper, maybe var innerFs = GetUnderlyingFileSystemNoCache(alias, fallback); - var shadowWrapper = new ShadowWrapper(innerFs, "typed/" + alias, IsScoped); + var shadowWrapper = new ShadowWrapper(innerFs, "typed/" + alias, () => IsScoped()); var fs = (IFileSystem) Activator.CreateInstance(typeof(TFileSystem), shadowWrapper); _wrappers.Add(shadowWrapper); // keeping a reference to the wrapper return fs; diff --git a/src/Umbraco.Core/IO/ShadowWrapper.cs b/src/Umbraco.Core/IO/ShadowWrapper.cs index 1a3eeea018..7467f87135 100644 --- a/src/Umbraco.Core/IO/ShadowWrapper.cs +++ b/src/Umbraco.Core/IO/ShadowWrapper.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.IO { _innerFileSystem = innerFileSystem; _shadowPath = shadowPath; - _isScoped = isScoped ?? (() => false); + _isScoped = isScoped; } internal void Shadow(Guid id) diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationContext.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationContext.cs index cfd1a1304a..850672a969 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationContext.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationContext.cs @@ -12,12 +12,9 @@ namespace Umbraco.Core.Persistence.Migrations { public MigrationContext(IUmbracoDatabase database, ILogger logger) { - if (database == null) throw new ArgumentNullException(nameof(database)); - if (logger == null) throw new ArgumentNullException(nameof(logger)); - Expressions = new Collection(); - Database = database; - Logger = logger; + Database = database ?? throw new ArgumentNullException(nameof(database)); + Logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public ICollection Expressions { get; set; } diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index b8fdb25119..3318cf81e7 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -31,6 +31,8 @@ namespace Umbraco.Core.Scoping // take control of the FileSystems _fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems; + + _scopeReference = new ScopeReference(this); } static ScopeProvider() @@ -230,7 +232,7 @@ namespace Umbraco.Core.Scoping internal const string ContextItemKey = "Umbraco.Core.Scoping.ScopeContext"; - internal static ScopeContext AmbientContextStatic + public ScopeContext AmbientContext { get { @@ -251,9 +253,6 @@ namespace Umbraco.Core.Scoping } } - /// - public ScopeContext AmbientContext => AmbientContextStatic; - #endregion #region Ambient Scope @@ -262,10 +261,9 @@ namespace Umbraco.Core.Scoping internal const string ScopeRefItemKey = "Umbraco.Core.Scoping.ScopeReference"; // only 1 instance which can be disposed and disposed again - // fixme - more weird static - we should try to get rid of all static & use an accessor - private static readonly ScopeReference StaticScopeReference = new ScopeReference(new ScopeProvider(null, null, null)); + private readonly ScopeReference _scopeReference; - private static Scope AmbientScopeStatic + public Scope AmbientScope { get { @@ -283,19 +281,12 @@ namespace Umbraco.Core.Scoping // set http/call context if (value.CallContext == false && SetHttpContextObject(ScopeItemKey, value, false)) - SetHttpContextObject(ScopeRefItemKey, StaticScopeReference); + SetHttpContextObject(ScopeRefItemKey, _scopeReference); else SetCallContextObject(ScopeItemKey, value); } } - /// - public Scope AmbientScope - { - get => AmbientScopeStatic; - set => AmbientScopeStatic = value; - } - #endregion public void SetAmbient(Scope scope, ScopeContext context = null) @@ -315,7 +306,7 @@ namespace Umbraco.Core.Scoping if (scope.CallContext == false && SetHttpContextObject(ScopeItemKey, scope, false)) { - SetHttpContextObject(ScopeRefItemKey, StaticScopeReference); + SetHttpContextObject(ScopeRefItemKey, _scopeReference); SetHttpContextObject(ContextItemKey, context); } else @@ -410,7 +401,7 @@ namespace Umbraco.Core.Scoping var scope = AmbientScope as Scope; scope?.Reset(); - StaticScopeReference.Dispose(); + _scopeReference.Dispose(); } ///