在 ASP.NET 5 中动态加载程序集

Dynamically load assemblies in ASP.NET 5

我曾经有一些代码扫描我的应用程序的 bin 目录以查找尚未加载到 AppDomain 中的程序集并加载它们。它基本上看起来像:

foreach (var assemblyPath in Directory.GetFiles("path\to\bin", "*.dll"))
{
    var inspected = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
    Assembly.Load(inspected.GetName());
}

为简洁起见,我跳过了 try/catch 子句等。

这允许我在 运行 时将程序集放入 bin 文件夹中,其中包含某些接口的实现,并让 IoC 容器自动拾取它们。现在有了新的 Roslyn 魔法,调试时不再有物理 DLL。有什么方法可以动态检索程序集名称、项目名称或依赖项名称(在 project.json 中)。

我想我必须实现类似 this example in the Entropy repo 的东西,但我不知道如何为我的场景实现它。

您正在寻找的是 ILibraryManager 实现,它提供对应用程序的完整依赖关系图的访问。这已经流经 ASP.NET 5 DI 系统。因此,您可以从那里联系它。

Sample usage can be found inside RoslynCompilationService.

您可以使用IAssemblyLoadContextAccessor 接口动态加载ASP.NET 5 class 个库(.xproj) 项目。以下示例代码适用于 Beta 4:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        var assemblyLoadContextAccessor = app.ApplicationServices.GetService<IAssemblyLoadContextAccessor>();
        var loadContext = assemblyLoadContextAccessor.Default;
        var loadedAssembly = loadContext.Load("NameOfYourLibrary");
    }
}

我按照@tugberk 的建议使用 ILibraryManager 部分解决了这个问题。我稍微改变了方法,从而不再需要扫描 bin 文件夹以查找新程序集。我只想要当前 AppDomain 中所有已加载的程序集。

我在我的类型查找器 class 中注入了一个 ILibraryManager 接口的实例,并使用了 GetReferencingLibraries() 方法和核心程序集的名称,它被所有其他程序引用应用程序中的程序集。

可以找到示例实现 here,这是重要的部分:

public IEnumerable<Assembly> GetLoadedAssemblies()
{
    return _libraryManager.GetReferencingLibraries(_coreAssemblyName.Name)
                          .SelectMany(info => info.Assemblies)
                          .Select(info => Assembly.Load(new AssemblyName(info.Name)));
}

对于 .net 核心用户,这是我从特定路径加载程序集的代码。我不得不使用指令,因为 .Net Framework 和 .Net Core 略有不同。

在您的 class header 中,您需要声明使用类似于:

#if NET46
#else
    using System.Runtime.Loader;
#endif

并且在您的函数中类似于以下内容:

string assemblyPath = "c:\temp\assmebly.dll";

#if NET46
                Assembly assembly = Assembly.LoadFrom(assemblyPath);
#else
                AssemblyLoadContext context = AssemblyLoadContext.Default;
                Assembly assembly = context.LoadFromAssemblyPath(assemblyPath);
#endif

它不是 ASP.NET,但可以轻松转换为 asp.net。 下面是用于加载程序集的函数,并在该程序集上的 class 中调用方法。

        private static FormCustomized loadLayout(global::System.String layoutFilename, global::System.String layoutNameSpace)
    {
        FormCustomized mainForm = default;
        Type typeMainLayout = default;
        FileInfo layoutFile;
        layoutFile = new FileInfo(layoutFilename);
        layoutFile.Refresh();
        if (!layoutFile.Exists)
        {
            MessageBox.Show("Layout file not found. You need to reinstall the program");
            return default;
        }

        try
        {
            Assembly assemblyRaw = Assembly.LoadFrom(layoutFilename);
            AssemblyLoadContext context = AssemblyLoadContext.Default;
            Assembly assembly = context.LoadFromAssemblyPath(layoutFilename);


            Type typeMainLayoutIni = assembly.GetType(layoutNameSpace + ".initializeLayoutClass");
            Object iniClass = Activator.CreateInstance(typeMainLayoutIni, true);
            MethodInfo methodInfo = typeMainLayoutIni.GetMethod("AssembliesToLoadAtStart");
            enVars.assemblies = (Dictionary<string, Environment.environmentAssembliesClass>)methodInfo.Invoke(iniClass, default);
            typeMainLayout = assembly.GetType(layoutNameSpace + ".mainAppLayoutForm");
            mainForm = Activator.CreateInstance(typeMainLayout, enVars) as FormCustomized;
        }
        catch (Exception ex)
        {
            return default;
        }

        return default;
    }