diff --git a/src/Umbraco.ModelsBuilder.Embedded/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.ModelsBuilder.Embedded/DependencyInjection/UmbracoBuilderExtensions.cs
index 7c71181ebb..45ca4a9078 100644
--- a/src/Umbraco.ModelsBuilder.Embedded/DependencyInjection/UmbracoBuilderExtensions.cs
+++ b/src/Umbraco.ModelsBuilder.Embedded/DependencyInjection/UmbracoBuilderExtensions.cs
@@ -1,21 +1,12 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
-using System.Reflection;
-using System.Text.Encodings.Web;
+using System.Threading;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor;
-using Microsoft.AspNetCore.Mvc.Razor.Compilation;
-using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
-using Microsoft.AspNetCore.Razor.Language;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Razor;
-using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
@@ -24,7 +15,6 @@ using Umbraco.Core.DependencyInjection;
using Umbraco.Core.Events;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.ModelsBuilder.Embedded.Building;
-using Umbraco.ModelsBuilder.Embedded.DependencyInjection;
using Umbraco.Web.WebAssets;
/*
@@ -47,9 +37,15 @@ using Umbraco.Web.WebAssets;
* have already been resolved with the LazyInitializer in the RazorReferenceManager.
*
* The services that can be replaced are: IViewCompilerProvider (default is the internal RuntimeViewCompilerProvider) and
- * IViewCompiler (default is the internal RuntimeViewCompiler).
+ * IViewCompiler (default is the internal RuntimeViewCompiler). There is one specific public extension point that I was
+ * hoping would solve all of the problems which was IMetadataReferenceFeature (implemented by LazyMetadataReferenceFeature
+ * which uses RazorReferencesManager) which is a razor feature that you can add
+ * to the RazorProjectEngine. It is used to resolve roslyn references and by default is backed by RazorReferencesManager.
+ * Unfortunately, this service is not used by the CSharpCompiler, it seems to only be used by some tag helper compilations.
+ *
+ * There are caches at several levels, all of which are not publicly accessible APIs (apart from RazorViewEngine.ViewLookupCache
+ * which is possible to clear by casting and then calling cache.Compact(100); but that doesn't get us far enough).
*
- * There are caches at several levels, all of which are not publicly accessible APIs (apart from RazorViewEngine.ViewLookupCache).
* For this to work, several caches must be cleared:
* - RazorViewEngine.ViewLookupCache
* - RazorReferencesManager._compilationReferences
@@ -66,11 +62,17 @@ using Umbraco.Web.WebAssets;
* services from scratch which means there is no caches.
*
* ... Option C works, we will use that but need to verify how this affects memory since ideally the old services will be GC'd.
+ *
+ * Option C, how its done:
+ * - Before we add our custom razor services to the container, we make a copy of the services collection which is the snapshot of registered services
+ * with razor defaults before ours are added.
+ * - We replace the default implementation of IRazorViewEngine with our own. This is a wrapping service that wraps the default RazorViewEngine instance.
+ * The ctor for this service takes in a Factory method to re-construct the default RazorViewEngine and all of it's dependency graph.
+ * - When the PureLive models change, the Factory is invoked and the default razor services are all re-created, thus clearing their caches and the newly
+ * created instance is wrapped. The RazorViewEngine is the only service that needs to be replaced and wrapped for this to work because it's dependency
+ * graph includes all of the above mentioned services, all the way up to the RazorProjectEngine and it's LazyMetadataReferenceFeature.
*/
-// This is the insanity that allows you to customize the RazorProjectEngineBuilder
-[assembly: ProvideRazorExtensionInitializer("ModelsBuilderPureLive", typeof(ModelsBuilderRazorProjectBuilderExtension))]
-
namespace Umbraco.ModelsBuilder.Embedded.DependencyInjection
{
///
@@ -83,8 +85,9 @@ namespace Umbraco.ModelsBuilder.Embedded.DependencyInjection
///
public static IUmbracoBuilder AddModelsBuilder(this IUmbracoBuilder builder)
{
- builder.AddRazorProjectEngine();
+ builder.AddPureLiveRazorEngine();
builder.Services.AddSingleton();
+
// TODO: I feel like we could just do builder.AddNotificationHandler() and it
// would automatically just register for all implemented INotificationHandler{T}?
builder.AddNotificationHandler();
@@ -127,20 +130,8 @@ namespace Umbraco.ModelsBuilder.Embedded.DependencyInjection
return builder;
}
- private static IUmbracoBuilder AddRazorProjectEngine(this IUmbracoBuilder builder)
+ private static IUmbracoBuilder AddPureLiveRazorEngine(this IUmbracoBuilder builder)
{
- // TODO: This is super nasty, but we can at least tinker with this for now
- // this pre-builds the container just so we can extract the default RazorProjectEngine
- // in order to extract out all features and re-add them to ours
-
- // Since we cannot construct the razor engine like netcore does:
- // https://github.com/dotnet/aspnetcore/blob/336e05577cd8bec2000ffcada926189199e4cef0/src/Mvc/Mvc.Razor.RuntimeCompilation/src/DependencyInjection/RazorRuntimeCompilationMvcCoreBuilderExtensions.cs#L86
- // because many things are internal we need to resort to this which is to get the default RazorProjectEngine
- // that is nornally created and use that to create/wrap our custom one.
-
- ServiceProvider initialServices = builder.Services.BuildServiceProvider();
- RazorProjectEngine defaultRazorProjectEngine = initialServices.GetRequiredService();
-
// copy the current collection, we need to use this later to rebuild a container
// to re-create the razor compiler provider
var initialCollection = new ServiceCollection
@@ -148,29 +139,7 @@ namespace Umbraco.ModelsBuilder.Embedded.DependencyInjection
builder.Services
};
- //builder.Services.AddSingleton(
- // s => new RefreshingRazorProjectEngine(defaultRazorProjectEngine, s, s.GetRequiredService()));
-
- //builder.Services.AddSingleton(
- // s => new RefreshingRuntimeViewCompilerProvider(
- // () =>
- // {
- // // re-create the original container so that a brand new IViewCompilerProvider
- // // is produced, if we don't re-create the container then it will just return the same instance.
- // ServiceProvider recreatedServices = initialCollection.BuildServiceProvider();
- // return recreatedServices.GetRequiredService();
- // }, s.GetRequiredService()));
-
- //builder.Services.AddSingleton(
- // s => new RefreshingRazorPageActivator(
- // () =>
- // {
- // // re-create the original container so that a brand new IRazorPageActivator
- // // is produced, if we don't re-create the container then it will just return the same instance.
- // ServiceProvider recreatedServices = initialCollection.BuildServiceProvider();
- // return recreatedServices.GetRequiredService();
- // }, s.GetRequiredService()));
-
+ // Replace the default with our custom engine
builder.Services.AddSingleton(
s => new RefreshingRazorViewEngine(
() =>
@@ -181,106 +150,31 @@ namespace Umbraco.ModelsBuilder.Embedded.DependencyInjection
return recreatedServices.GetRequiredService();
}, s.GetRequiredService()));
- //builder.Services.AddSingleton();
-
return builder;
}
}
- internal class ModelsBuilderRazorProjectBuilderExtension : RazorExtensionInitializer
- {
- public override void Initialize(RazorProjectEngineBuilder builder)
- {
- // Finally, after jumping through many hoops, we can customize the builder.
-
- // Get our extension that launched this so we can access services
- ModelsBuilderAssemblyExtension mbExt = builder.Configuration.Extensions.OfType().First();
-
- // Now... customize
-
- builder.Phases.Add(new CustomRazorPhase());
-
- // NOTE: This is called before all of the default options that are applied
- // in AddRazorRuntimeCompilation, see https://github.com/dotnet/aspnetcore/blob/336e05577cd8bec2000ffcada926189199e4cef0/src/Mvc/Mvc.Razor.RuntimeCompilation/src/DependencyInjection/RazorRuntimeCompilationMvcCoreBuilderExtensions.cs#L88
- // are done so you can't replace anything here that is added by the default razor runtime compilation.
- }
- }
-
- internal class CustomRazorPhase : RazorEnginePhaseBase, IRazorEnginePhase
- {
- protected override void ExecuteCore(RazorCodeDocument codeDocument)
- {
- // it's possible to modify the razor generated document with custom phases.
- // like possibly setting default import statements, etc..
- // there's no documentation on this so you'll need to read the source code to figure
- // that one out if we ever wanted it.
- }
- }
-
- // We need a custom assembly extension so we can pass state into the initializer :/
- internal class ModelsBuilderAssemblyExtension : AssemblyExtension
- {
- public ModelsBuilderAssemblyExtension(IServiceProvider serviceProvider, string extensionName, Assembly assembly)
- : base(extensionName, assembly) => ServiceProvider = serviceProvider;
-
- public IServiceProvider ServiceProvider { get; }
- }
-
- //// The default razor page activator keeps an internal cache of activations, this allows clearning that cache
- //// TODO: Find out if we really need to clear this cache or not? Or if just clearing the view engine cache is enough?
- //internal class RefreshingRazorPageActivator : IRazorPageActivator
- //{
- // private readonly Func _defaultRazorPageActivatorFactory;
- // private readonly PureLiveModelFactory _pureLiveModelFactory;
- // private IRazorPageActivator _current;
-
- // public RefreshingRazorPageActivator(
- // Func defaultRazorPageActivatorFactory,
- // PureLiveModelFactory pureLiveModelFactory)
- // {
- // _pureLiveModelFactory = pureLiveModelFactory;
- // _defaultRazorPageActivatorFactory = defaultRazorPageActivatorFactory;
- // _current = _defaultRazorPageActivatorFactory();
- // _pureLiveModelFactory.ModelsChanged += PureLiveModelFactory_ModelsChanged;
- // }
-
- // // TODO: Do we need to lock?
- // private void PureLiveModelFactory_ModelsChanged(object sender, EventArgs e) => _current = _defaultRazorPageActivatorFactory();
-
- // public void Activate(IRazorPage page, ViewContext context) => _current.Activate(page, context);
- //}
-
- // We need to have a refreshing razor view engine - the default keeps an in memory cache of views and it cannot be cleared because
- // the cache key instance is internal and would require manually tracking all keys since it cannot be iterated.
- // So like other 'Refreshing' intances, we just create a brand new one and let the old one die therefore clearing the cache.
-
- // TODO: It looks like dynamic recompile works just fine with "only" the refreshing razor view engine BUT
- // that's also because it creates a new instance of the IRazorPageActivator since when resolving the engine
- // again it's of course going to resolve all dependencies as well which is a few:
- // https://github.com/dotnet/aspnetcore/blob/e37ddbcdbc445a65c6f51549775d5924423880e4/src/Mvc/Mvc.Razor/src/RazorViewEngine.cs#L51
- // which isn't ideal.
- // There's no real way to clear the cache since we cannot iterate and have no access to the cache key instance
-
- internal class RefreshingRazorViewEngine : /*RazorViewEngine,*/ IRazorViewEngine
+ ///
+ /// Custom that wraps aspnetcore's default implementation
+ ///
+ ///
+ /// This is used so that when new PureLive models are built, the entire razor stack is re-constructed so all razor
+ /// caches and assembly references, etc... are cleared.
+ ///
+ internal class RefreshingRazorViewEngine : IRazorViewEngine
{
private IRazorViewEngine _current;
private readonly PureLiveModelFactory _pureLiveModelFactory;
private readonly Func _defaultRazorViewEngineFactory;
+ private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
- //public RefreshingRazorViewEngine(
- // PureLiveModelFactory pureLiveModelFactory,
- // IRazorPageFactoryProvider pageFactory,
- // IRazorPageActivator pageActivator,
- // HtmlEncoder htmlEncoder,
- // IOptions optionsAccessor,
- // ILoggerFactory loggerFactory,
- // DiagnosticListener diagnosticListener)
- // : base(pageFactory, pageActivator, htmlEncoder, optionsAccessor, loggerFactory, diagnosticListener)
- //{
- // _pureLiveModelFactory = pureLiveModelFactory;
- // _pureLiveModelFactory.ModelsChanged += PureLiveModelFactory_ModelsChanged;
- //}
-
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A factory method used to re-construct the default aspnetcore
+ ///
+ /// The
public RefreshingRazorViewEngine(Func defaultRazorViewEngineFactory, PureLiveModelFactory pureLiveModelFactory)
{
_pureLiveModelFactory = pureLiveModelFactory;
@@ -289,275 +183,86 @@ namespace Umbraco.ModelsBuilder.Embedded.DependencyInjection
_pureLiveModelFactory.ModelsChanged += PureLiveModelFactory_ModelsChanged;
}
- // TODO: Do we need to lock?
+ ///
+ /// When the pure live models change, re-construct the razor stack
+ ///
private void PureLiveModelFactory_ModelsChanged(object sender, EventArgs e)
{
- //var cache = (Microsoft.Extensions.Caching.Memory.MemoryCache)ViewLookupCache;
- //// clear 100% of the cache.
- //// TODO: This seems to work but need to verify
- //cache.Compact(100);
-
- _current = _defaultRazorViewEngineFactory();
- }
-
- public RazorPageResult FindPage(ActionContext context, string pageName) => _current.FindPage(context, pageName);
-
- public string GetAbsolutePath(string executingFilePath, string pagePath) => _current.GetAbsolutePath(executingFilePath, pagePath);
-
- public RazorPageResult GetPage(string executingFilePath, string pagePath) => _current.GetPage(executingFilePath, pagePath);
-
- public ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage) => _current.FindView(context, viewName, isMainPage);
-
- public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isMainPage) => _current.GetView(executingFilePath, viewPath, isMainPage);
- }
-
- //// The default view compiler creates the compiler once and only once. That compiler will have a stale list of references
- //// to build against so it needs to be re-created. The only way to do that due to internals is to wrap it and re-create
- //// the default instance therefore resetting the compiler references.
- //// TODO: Find out if we really need to clear this cache or not? Or if just clearing the view engine cache is enough?
- //internal class RefreshingRuntimeViewCompilerProvider : IViewCompilerProvider
- //{
- // private IViewCompilerProvider _current;
- // private readonly Func _defaultViewCompilerProviderFactory;
- // private readonly PureLiveModelFactory _pureLiveModelFactory;
-
- // public RefreshingRuntimeViewCompilerProvider(
- // Func defaultViewCompilerProviderFactory,
- // PureLiveModelFactory pureLiveModelFactory)
- // {
- // _defaultViewCompilerProviderFactory = defaultViewCompilerProviderFactory;
- // _pureLiveModelFactory = pureLiveModelFactory;
- // _current = _defaultViewCompilerProviderFactory();
- // _pureLiveModelFactory.ModelsChanged += PureLiveModelFactory_ModelsChanged;
- // }
-
- // // TODO: Do we need to lock?
- // private void PureLiveModelFactory_ModelsChanged(object sender, EventArgs e) => _current = _defaultViewCompilerProviderFactory();
-
- // public IViewCompiler GetCompiler() => _current.GetCompiler();
- //}
-
- // TODO: Need to review this to see if this service is actually one we need to clear or not?
- // Does it hold cache? etc... I originally said "so that all of the underlying services are cleared"
- // but I'm not sure now if that's needed since we need the above which do hold caches.
- // The other problem is that because we are re-creating the above services from the default service collection,
- // the reference they will have for their RazorProjectEngine will not be this one, it will be the default one...
- // though I guess we can change that based on the ordering and resolving of the containers.
- // I'm just not entirely convinced we need this anymore?
- // ... Actually, it might be all related to this IMetadataReferenceFeature thing since that sort of needs to be refreshed.
- // guess we need to do some testing. But we need to ensure that there's not a bunch of different razor project engines being
- // referenced everywhere.
- internal class RefreshingRazorProjectEngine : RazorProjectEngine
- {
- private RazorProjectEngine _current;
- private readonly RazorProjectEngine _defaultRazorProjectEngine;
- private readonly IServiceProvider _serviceProvider;
- private readonly PureLiveModelFactory _pureLiveModelFactory;
- private readonly MethodInfo _createCodeDocumentCore;
- private readonly MethodInfo _createCodeDocumentDesignTimeCore;
- private readonly MethodInfo _processCore;
-
- public RefreshingRazorProjectEngine(
- RazorProjectEngine defaultRazorProjectEngine,
- IServiceProvider serviceProvider,
- PureLiveModelFactory pureLiveModelFactory)
- {
- _defaultRazorProjectEngine = defaultRazorProjectEngine;
- _serviceProvider = serviceProvider;
- _pureLiveModelFactory = pureLiveModelFactory;
- _current = CreateNew();
- Type engineType = _current.GetType();
- _createCodeDocumentCore = engineType.GetMethod(nameof(CreateCodeDocumentCore), BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(RazorProjectItem) }, null);
- _createCodeDocumentDesignTimeCore = engineType.GetMethod(nameof(CreateCodeDocumentDesignTimeCore), BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(RazorProjectItem) }, null);
- _processCore = engineType.GetMethod(nameof(ProcessCore), BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(RazorCodeDocument) }, null);
-
- _pureLiveModelFactory.ModelsChanged += PureLiveModelFactory_ModelsChanged;
- }
-
- public override RazorConfiguration Configuration => _current.Configuration;
-
- public override RazorProjectFileSystem FileSystem => _current.FileSystem;
-
- public override RazorEngine Engine => _current.Engine;
-
- public override IReadOnlyList ProjectFeatures => _current.ProjectFeatures;
-
- public override RazorCodeDocument Process(RazorProjectItem projectItem) => base.Process(projectItem);
-
- public override RazorCodeDocument Process(RazorSourceDocument source, string fileKind, IReadOnlyList importSources, IReadOnlyList tagHelpers) => base.Process(source, fileKind, importSources, tagHelpers);
-
- public override RazorCodeDocument ProcessDeclarationOnly(RazorProjectItem projectItem) => base.ProcessDeclarationOnly(projectItem);
-
- public override RazorCodeDocument ProcessDeclarationOnly(RazorSourceDocument source, string fileKind, IReadOnlyList importSources, IReadOnlyList tagHelpers) => base.ProcessDeclarationOnly(source, fileKind, importSources, tagHelpers);
-
- public override RazorCodeDocument ProcessDesignTime(RazorProjectItem projectItem) => base.ProcessDesignTime(projectItem);
-
- public override RazorCodeDocument ProcessDesignTime(RazorSourceDocument source, string fileKind, IReadOnlyList importSources, IReadOnlyList tagHelpers) => base.ProcessDesignTime(source, fileKind, importSources, tagHelpers);
-
- // TODO: Do we need to lock?
- private void PureLiveModelFactory_ModelsChanged(object sender, EventArgs e) => _current = CreateNew();
-
- private RazorConfiguration GetConfiguration()
- {
- RazorConfiguration defaultConfig = RazorConfiguration.Default;
-
- // TODO: It turns out you can add logic into the RazorProjectEngineBuilder by adding custom razor extensions to the configuration.
- var mbConfig = RazorConfiguration.Create(
- defaultConfig.LanguageVersion,
- "ModelsBuilderPureLiveConfig",
- new[] { new ModelsBuilderAssemblyExtension(_serviceProvider, "ModelsBuilderPureLive", typeof(RefreshingRazorProjectEngine).Assembly) });
-
- return mbConfig;
- }
-
- private RazorProjectEngine CreateNew()
- {
- // get the default
- RazorProjectFileSystem fileSystem = _serviceProvider.GetRequiredService();
-
- // Create the project engine
- var projectEngine = RazorProjectEngine.Create(GetConfiguration(), fileSystem, builder =>
+ _locker.EnterWriteLock();
+ try
{
- // TODO: All this hacking is because RazorExtensionInitializer doesn't give us access to the service provider
-
- // replace all features with the defaults
- builder.Features.Clear();
-
- foreach (IRazorEngineFeature f in _defaultRazorProjectEngine.EngineFeatures)
- {
- builder.Features.Add(f);
- }
-
- foreach (IRazorProjectEngineFeature f in _defaultRazorProjectEngine.ProjectFeatures)
- {
- builder.Features.Add(f);
- }
-
- // The razor engine only supports one instance of IMetadataReferenceFeature
- // so we need to jump through some hoops to allow multiple by using a wrapper.
- // so get the current ones, remove them from the list, create a wrapper of them and
- // our custom one and then add it back.
- var metadataReferenceFeatures = builder.Features.OfType().ToList();
- foreach (IMetadataReferenceFeature m in metadataReferenceFeatures)
- {
- builder.Features.Remove(m);
- }
-
- // add our custom one to the list
- metadataReferenceFeatures.Add(new PureLiveMetadataReferenceFeature(_pureLiveModelFactory));
-
- // now add them to our wrapper and back into the features
- builder.Features.Add(new MetadataReferenceFeatureWrapper(metadataReferenceFeatures));
- });
-
- return projectEngine;
- }
-
- protected override RazorCodeDocument CreateCodeDocumentCore(RazorProjectItem projectItem)
- => (RazorCodeDocument)_createCodeDocumentCore.Invoke(_current, new[] { projectItem });
-
- protected override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem)
- => (RazorCodeDocument)_createCodeDocumentDesignTimeCore.Invoke(_current, new[] { projectItem });
-
- protected override void ProcessCore(RazorCodeDocument codeDocument)
- => _processCore.Invoke(_current, new[] { codeDocument });
- }
-
- ///
- /// Wraps multiple
- ///
- ///
- ///
- /// This is required because the razor engine only supports a single IMetadataReferenceFeature but their APIs don't state this,
- /// this is purely down to them doing this 'First' call: https://github.com/dotnet/aspnetcore/blob/b795ac3546eb3e2f47a01a64feb3020794ca33bb/src/Razor/Microsoft.CodeAnalysis.Razor/src/CompilationTagHelperFeature.cs#L37
- /// So in order to have multiple, we need to have a wrapper.
- ///
- ///
- internal class MetadataReferenceFeatureWrapper : IMetadataReferenceFeature
- {
- private readonly IReadOnlyList _metadataReferenceFeatures;
- private RazorEngine _engine;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public MetadataReferenceFeatureWrapper(IEnumerable metadataReferenceFeatures)
- => _metadataReferenceFeatures = metadataReferenceFeatures.ToList();
-
- ///
- public IReadOnlyList References
- => _metadataReferenceFeatures.SelectMany(x => x.References).ToList();
-
- ///
- public RazorEngine Engine
- {
- get => _engine;
- set
- {
- _engine = value;
- foreach (IMetadataReferenceFeature feature in _metadataReferenceFeatures)
- {
- feature.Engine = value;
- }
+ _current = _defaultRazorViewEngineFactory();
}
- }
- }
-
- ///
- /// A custom that will dynamically resolve a reference for razor (tag helpers) based on the current PureLive assembly.
- ///
- internal class PureLiveMetadataReferenceFeature : IMetadataReferenceFeature
- {
- /*
- * TODO:
- * This is the only public API available to 'refresh' without hacking is the IMetadataReferenceFeature but
- * it's not clear what this does since it's only used by a single service in aspnetcore.
- * It is not responsible for recompiling views, but is for tag helpers.
- * Used in the CompilationTagHelperFeature which ends up setting the 'Compilation' on the TagDescriptorProviderContext and
- * the result of GetDescriptors() is used in the DefaultRazorTagHelperBinderPhase.
- * This is all about compiling tag helpers.
- * So am thinking we'll need to 'refresh' this feature as well as the normal razor views.
- */
-
- private readonly PureLiveModelFactory _pureLiveModelFactory;
- private MetadataReference[] _pureLiveReferences;
-
- public PureLiveMetadataReferenceFeature(PureLiveModelFactory pureLiveModelFactory)
- {
- _pureLiveModelFactory = pureLiveModelFactory;
- _pureLiveModelFactory.ModelsChanged += PureLiveModelFactory_ModelsChanged;
- }
-
- ///
- public IReadOnlyList References
- {
- get
+ finally
{
- // return the reference if we have one
- if (_pureLiveReferences != null)
- {
- return _pureLiveReferences;
- }
-
- // else check if we need to create the reference
- if (_pureLiveModelFactory.CurrentModelsMetadataReference != null)
- {
- _pureLiveReferences = new[] { _pureLiveModelFactory.CurrentModelsMetadataReference };
-
- return _pureLiveReferences;
- }
-
- return Array.Empty();
+ _locker.ExitWriteLock();
}
}
- ///
- public RazorEngine Engine { get; set; }
+ public RazorPageResult FindPage(ActionContext context, string pageName)
+ {
+ _locker.EnterReadLock();
+ try
+ {
+ return _current.FindPage(context, pageName);
+ }
+ finally
+ {
+ _locker.ExitReadLock();
+ }
+ }
- ///
- /// When the models change, clear our references
- ///
- // TODO: Determine what happens without locking this, will it matter?
- private void PureLiveModelFactory_ModelsChanged(object sender, EventArgs e) => _pureLiveReferences = null;
+ public string GetAbsolutePath(string executingFilePath, string pagePath)
+ {
+ _locker.EnterReadLock();
+ try
+ {
+ return _current.GetAbsolutePath(executingFilePath, pagePath);
+ }
+ finally
+ {
+ _locker.ExitReadLock();
+ }
+ }
+
+ public RazorPageResult GetPage(string executingFilePath, string pagePath)
+ {
+ _locker.EnterReadLock();
+ try
+ {
+ return _current.GetPage(executingFilePath, pagePath);
+ }
+ finally
+ {
+ _locker.ExitReadLock();
+ }
+ }
+
+ public ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage)
+ {
+ _locker.EnterReadLock();
+ try
+ {
+ return _current.FindView(context, viewName, isMainPage);
+
+ }
+ finally
+ {
+ _locker.ExitReadLock();
+ }
+ }
+
+ public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isMainPage)
+ {
+ _locker.EnterReadLock();
+ try
+ {
+ return _current.GetView(executingFilePath, viewPath, isMainPage);
+ }
+ finally
+ {
+ _locker.ExitReadLock();
+ }
+ }
}
}