I'm using assembly loading context to load plugin web apps in my host app (previously I used AppDomains)
public class PluginAssemblyLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
private IDictionary<string, Assembly> _loadedAssemblies;
private IDictionary<string, Assembly> _sharedAssemblies;
private string _path;
/// <summary>
///
/// </summary>
/// <param name="path">assembly file path</param>
/// <param name="sharedTypes"></param>
public PluginAssemblyLoadContext(string path, params Type[] sharedTypes)
: base(isCollectible: false) // TODO: make collectible
{
_path = path;
var fileInfo = new FileInfo(_path);
if (fileInfo.Exists)
{
_resolver = new AssemblyDependencyResolver(_path);
_sharedAssemblies = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
foreach (var t in sharedTypes)
{
LoadReferencedAssemblies(t.Assembly);
}
_loadedAssemblies = new Dictionary<string, Assembly>();
var assembly = LoadFromAssemblyPath(fileInfo.FullName);
_loadedAssemblies.Add(fileInfo.FullName, assembly);
}
else
{
throw new FileNotFoundException($"File does not exist: {_path}");
}
}
public bool LoadReferencedAssemblies(Assembly assembly)
{
if (!_sharedAssemblies.ContainsKey(assembly.Location))
{
_sharedAssemblies.Add(assembly.Location, assembly);
foreach (var an in assembly.GetReferencedAssemblies())
{
var ra = Assembly.Load(an);
LoadReferencedAssemblies(ra);
}
return true;
}
else
{
return false;
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IEnumerable<Type> GetCommandTypes<T>()
{
var cmdType = typeof(T);
return _loadedAssemblies.Values.SelectMany(a => a.GetTypes()).Where(t => cmdType.IsAssignableFrom(t));
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assembly"></param>
/// <returns></returns>
public IEnumerable<T> CreateCommands<T>(Assembly assembly)
{
foreach (var cmdType in GetCommandTypes<T>())
{
yield return (T)Activator.CreateInstance(cmdType);
}
}
protected override Assembly Load(AssemblyName assemblyName)
{
var path = _resolver.ResolveAssemblyToPath(assemblyName);
if (path != null)
{
if (_sharedAssemblies.ContainsKey(path))
{
return _sharedAssemblies[path];
// return null; // should be the same (using default ALC)
}
if (_loadedAssemblies.ContainsKey(path))
{
return _loadedAssemblies[path];
}
return LoadFromAssemblyPath(path);
}
return null; // using default ALC
}
}
The problem is that razor cant load load views normally.
I'm getting:
System.ArgumentNullException: Value cannot be null. (Parameter 'item')
at Microsoft.AspNetCore.Mvc.Razor.Compilation.CompiledViewDescriptor..ctor(RazorCompiledItem item)
at Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RuntimeViewCompiler.CompileAndEmit(String relativePath)
at Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RuntimeViewCompiler.OnCacheMiss(String normalizedPath)
I've used dnSpy and detected that view assembly generation works ok, but RazorCompiledItemLoader.LoadItems(assembly) gives 0 items because assembly.GetCustomAttributes() gives 0 attributes.
But RazorCompiledItemAttribute is actually there! I've saved view assembly via dnSpy and dumped to disk. When I'am trying to GetCustomAttributes inside plugin during ConfigureServices(IServiceCollection services)... (for example) I'm getting 0 matching attributes. But outside plugin call I.m getting 1 matching attribute! How can I make razor load this RazorCompiledItemAttribute?
Aucun commentaire:
Enregistrer un commentaire