Files
Umbraco-CMS/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/InMemoryAssemblyLoadContextManager.cs
Mole 94774113f6 V11: Fix InMemoryAuto modelsbuilder mode (#13107)
* POC of a solution that works

* Add razor reference manager

* Ensure the compilation options are correct

* Move InMemory classes to its own namespace

These are all internal, so it should be fine.

* Throw proper exceptions when compilation fails

* Add CheckSumValidator

* Clear the ViewCompiler cache when models changed

This means we no longer need the RefreshingRazorViewEngine \o/

* Remove unused constructor injection

* Make UmbracoAssemblyLoadContext non internal

* Add WIP

* Clear the RazorViewEngine cache when generating new models

This uses reflection, which isn't super nice, however, the alternative is to clone'n'own the entire RazorViewEngine, which is arguably worse

* Fix circular dependency

* Remove ModelsChanged event

This is no longer necessary

* Fix precompiled views path

We need to normalize these paths to ensure they matches with the keys in _precompiledViews

* Clean

* Fix content tests

* Add logging

* Update the comment in UmbracoBuilderDependencyInjectionExtensions to reflect changes

* Remove RefreshingRazorViewEngine as its no longer needed

* Remove unused ViewEngine hack from DI

* Fix langversion

This is required since dotnet 7 is still in preview

* Add modelsbuilder tests

* Add more tests

* fixed comment

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-10-07 10:42:32 +02:00

67 lines
2.7 KiB
C#

using System.Reflection;
using System.Runtime.Loader;
using Umbraco.Cms.Infrastructure.ModelsBuilder;
namespace Umbraco.Cms.Web.Common.ModelsBuilder.InMemoryAuto;
internal class InMemoryAssemblyLoadContextManager
{
private UmbracoAssemblyLoadContext? _currentAssemblyLoadContext;
public InMemoryAssemblyLoadContextManager() =>
AssemblyLoadContext.Default.Resolving += OnResolvingDefaultAssemblyLoadContext;
private string? _modelsAssemblyLocation;
public string? ModelsAssemblyLocation => _modelsAssemblyLocation;
/// <summary>
/// Handle the event when a reference cannot be resolved from the default context and return our custom MB assembly reference if we have one
/// </summary>
/// <remarks>
/// This is required because the razor engine will only try to load things from the default context, it doesn't know anything
/// about our context so we need to proxy.
/// </remarks>
private Assembly? OnResolvingDefaultAssemblyLoadContext(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
=> assemblyName.Name == RoslynCompiler.GeneratedAssemblyName
? _currentAssemblyLoadContext?.LoadFromAssemblyName(assemblyName)
: null;
internal void RenewAssemblyLoadContext()
{
// If there's a current AssemblyLoadContext, unload it before creating a new one.
_currentAssemblyLoadContext?.Unload();
// We must create a new assembly load context
// as long as theres a reference to the assembly load context we can't delete the assembly it loaded
_currentAssemblyLoadContext = new UmbracoAssemblyLoadContext();
}
/// <summary>
/// Loads an assembly into the collectible assembly used by the factory
/// </summary>
/// <remarks>
/// This is essentially just a wrapper around the <see cref="UmbracoAssemblyLoadContext"/>,
/// because we don't want to allow other clases to take a reference on the AssemblyLoadContext
/// </remarks>
/// <returns>The loaded assembly</returns>
public Assembly LoadCollectibleAssemblyFromStream(Stream assembly, Stream? assemblySymbols)
{
_currentAssemblyLoadContext ??= new UmbracoAssemblyLoadContext();
return _currentAssemblyLoadContext.LoadFromStream(assembly, assemblySymbols);
}
public Assembly LoadCollectibleAssemblyFromPath(string path)
{
_currentAssemblyLoadContext ??= new UmbracoAssemblyLoadContext();
return _currentAssemblyLoadContext.LoadFromAssemblyPath(path);
}
public Assembly LoadModelsAssembly(string path)
{
Assembly assembly = LoadCollectibleAssemblyFromPath(path);
_modelsAssemblyLocation = assembly.Location;
return assembly;
}
}