我正在使用程序集加载上下文在我的主机应用程序中加载插件 Web 应用程序(以前我使用过 AppDomains)
public class PluginAssemblyLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
private IDictionary<string, Assembly> _loadedAssemblies;
private IDictionary<string, Assembly> _sharedAssemblies;
private AssemblyLoadContext DefaultAlc;
private string _path;
public PluginAssemblyLoadContext(string path, bool isCollectible, params Type[] sharedTypes)
: this(path, isCollectible, sharedTypes.Select(t => t.Assembly).ToArray())
{
}
public PluginAssemblyLoadContext(string path, bool isCollectible, params Assembly[] sharedAssemblies)
: base(isCollectible: isCollectible)
{
_path = path;
DefaultAlc = GetLoadContext(Assembly.GetExecutingAssembly()) ?? Default;
var fileInfo = new FileInfo(_path);
if (fileInfo.Exists)
{
_resolver = new AssemblyDependencyResolver(_path);
_sharedAssemblies = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
foreach (var a in sharedAssemblies.Distinct())
{
LoadReferencedAssemblies(a);
}
_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 (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (string.IsNullOrEmpty(assembly.Location))
{
throw new NotSupportedException($"Assembly location is empty string or null: {assembly.FullName}");
}
var alc = GetLoadContext(assembly);
if (alc == this)
{
throw new InvalidOperationException($"Circular assembly loader dependency detected");
}
if (!_sharedAssemblies.ContainsKey(assembly.Location))
{
_sharedAssemblies.Add(assembly.Location, assembly);
foreach (var an in assembly.GetReferencedAssemblies())
{
var ra = alc.LoadFromAssemblyName(an);
LoadReferencedAssemblies(ra);
}
return true;
}
else
{
return false;
}
}
public IEnumerable<Type> GetCommandTypes<T>()
{
var cmdType = typeof(T);
return _loadedAssemblies.Values.SelectMany(a => a.GetTypes()).Where(t => cmdType.IsAssignableFrom(t));
}
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];
}
if (_loadedAssemblies.ContainsKey(path))
{
return _loadedAssemblies[path];
}
return LoadFromAssemblyPath(path);
}
return DefaultAlc.LoadFromAssemblyName(assemblyName);
}
}
问题是剃须刀无法正常加载加载视图。
我越来越:
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)
我使用了 dnSpy 并检测到视图程序集生成工作正常,但是 RazorCompiledItemLoader.LoadItems(assembly) 给出了 0 个项目,因为 assembly.GetCustomAttributes() 给出了 0 个属性。
但是 RazorCompiledItemAttribute 实际上是存在的!我已经通过 dnSpy 保存了视图程序集并转储到磁盘。当我在 ConfigureServices(IServiceCollection services) 期间尝试在插件内部获取自定义属性时...(例如)我得到 0 个匹配的属性。但是外部插件调用我得到 1 个匹配属性!如何让剃刀加载这个 RazorCompiledItemAttribute?