Редактировать: Я загрузил исходный код проблемы в GitHub, если вы хотите загрузить: https://github.com/bryanenroute/assemblyloadcontext-issue
У меня есть консольное приложение .NET Core 3.0, которое ссылается на стандарт .NETБиблиотека классов 2.0 с единым интерфейсом (IModule). У меня также есть приложение ASP.NET Core 3.0, которое ссылается на ту же библиотеку классов .NET Standard 2.0 и реализует интерфейс (Module: IModule).
Я пытаюсь загрузить сборку ASP.NET Core из. Консольное приложение NET Core, использующее пользовательский AssemblyLoadContext и общий интерфейс библиотеки классов (IModule) ... простая система плагинов.
К сожалению, модуль / плагин ASP.NET Core не работает в функции переопределения ALC для Load (AssemblyName) со следующим исключением:
Не удалось загрузить файл или сборку 'Microsoft.AspNetCore.Components, версия = 3.0.0.0, культура = нейтральная, PublicKeyToken = adb9793829ddae60'. Системе не удается найти указанный файл.
Когда я пытаюсь использовать другой тип проекта (например, .NET Core Console Application или .NET Standard 2.0 Class Library), модуль / плагин загружается, как предполагалось.
Вот код консольного приложения:
using NetStandardCommon;
using System;
using System.IO;
namespace NetCoreConsoleApp
{
class Program
{
static void Main(string[] args)
{
LoadNetCoreModule();
LoadAspNetCoreModule();
}
static void LoadNetCoreModule()
{
//Works!
FileInfo asm = new FileInfo(@"..\..\..\..\NetCoreModule\bin\debug\netcoreapp3.0\NetCoreModule.dll");
var moduleDirectory = asm.DirectoryName;
ModuleAssemblyLoadContext context = new ModuleAssemblyLoadContext(asm.Name, moduleDirectory, typeof(IModule));
context.Scan();
foreach (var module in context.GetImplementations<IModule>())
{
module.Start();
}
}
static void LoadAspNetCoreModule()
{
//Fails!
FileInfo asm = new FileInfo(@"..\..\..\..\AspNetCoreApp\bin\debug\netcoreapp3.0\AspNetCoreApp.dll");
var moduleDirectory = asm.DirectoryName;
ModuleAssemblyLoadContext context = new ModuleAssemblyLoadContext(asm.Name, moduleDirectory, typeof(IModule));
context.Scan();
foreach (var module in context.GetImplementations<IModule>())
{
module.Start();
}
}
}
}
Вот код ModuleAssemblyLoadContext:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
using System.Linq;
namespace NetCoreConsoleApp
{
public class ModuleAssemblyLoadContext : AssemblyLoadContext
{
private List<Assembly> _loaded;
private Dictionary<string, Assembly> _shared;
private string _path;
private AssemblyDependencyResolver _resolver;
public ModuleAssemblyLoadContext(string name, string path, params Type[] sharedTypes) : base(name)
{
_path = path;
_resolver = new AssemblyDependencyResolver(_path);
_loaded = new List<Assembly>();
_shared = new Dictionary<string, Assembly>();
if (sharedTypes != null)
{
foreach (Type sharedType in sharedTypes)
{
_shared[Path.GetFileName(sharedType.Assembly.Location)] = sharedType.Assembly;
}
}
}
public void Scan()
{
foreach (string dll in Directory.EnumerateFiles(_path, "*.dll"))
{
var file = Path.GetFileName(dll);
if (_shared.ContainsKey(file))
{
continue;
}
var asm = this.LoadFromAssemblyPath(dll);
_loaded.Add(asm);
}
}
public IEnumerable<T> GetImplementations<T>()
{
return _loaded
.SelectMany(a => a.GetTypes())
.Where(t => typeof(T).IsAssignableFrom(t))
.Select(t => Activator.CreateInstance(t))
.Cast<T>();
}
protected override Assembly Load(AssemblyName assemblyName)
{
string filename = $"{assemblyName.Name}.dll";
if (_shared.ContainsKey(filename))
{
return _shared[filename];
}
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
{
return LoadUnmanagedDllFromPath(libraryPath);
}
return IntPtr.Zero;
}
}
}
Я попытался изменить функцию загрузки ALC для загрузки сборок непосредственно из общей папки. (C: \ Program Files (x86) \ dotnet \ shared \ Microsoft.AspNetCore.App \ 3.0.0), которая позволяет продолжить выполнение немного дальше, но в конечном итоге происходит сбой со следующим исключением:
Была предпринята попытка загрузить программу с неверным форматом. (0x8007000B)
Вот пересмотренная функция загрузки:
protected override Assembly Load(AssemblyName assemblyName)
{
string filename = $"{assemblyName.Name}.dll";
if (_shared.ContainsKey(filename))
{
return _shared[filename];
}
try
{
if (File.Exists(@"C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App\3.0.0\" + filename))
{
return Assembly.LoadFrom(@"C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App\3.0.0\" + filename);
}
}
catch (Exception ex)
{
//Message displayed is 'An attempt was made to load a program with an incorrect format. (0x8007000B)'
Console.WriteLine(ex.Message);
}
return Assembly.Load(assemblyName);
}
Я в восторге от возможностей загрузки / выгрузки сборок для системы плагинов .net Core, но яЯ изо всех сил пытаюсь преодолеть это препятствие. Чего мне не хватает?