【问题标题】:Dynamically load assemblies in ASP.NET 5在 ASP.NET 5 中动态加载程序集
【发布时间】:2015-06-07 21:14:16
【问题描述】:

我曾经有一些代码扫描我的应用程序的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 的东西,但我不知道如何为我的场景实现它。

【问题讨论】:

  • “不再有物理 DLL”是什么意思?在 ASP.NET5 之前,Roslyn 没有什么神奇之处。
  • @PauloMorgado 代码现在在调试时在内存中编译。它可能仍然适用于已发布的应用程序,但我希望它能够双向工作。
  • 代码一直在内存中编译。它只是保存到磁盘并加载它们 - 不同的编译器,不同的 API。尽管如此,网站上编译的内容是在一个程序集中,其他程序集是按需加载的。发生了什么变化?
  • @PauloMorgado 我说的是程序集被保存到磁盘。这让我可以扫描 bin 文件夹并动态加载程序集。
  • 好吧,我们中的一个人完全错了。

标签: c# asp.net-core c#-6.0


【解决方案1】:

您可以使用IAssemblyLoadContextAccessor 接口动态加载 ASP.NET 5 类库 (.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");
    }
}

【讨论】:

  • 虽然这可行,但问题的主要部分是解决要动态加载的程序集,而不是像您的示例中那样对 NameOfYourLibrary 进行硬编码。尽管如此,提到IAssemblyLoadContextAccessor
【解决方案2】:

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

Sample usage 可以在RoslynCompilationService 中找到。

【讨论】:

    【解决方案3】:

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

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

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

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

    【讨论】:

    • 您应该避免加载所有程序集,并尽可能以不这样做的方式使用 ILibraryManager。
    • @davidfowl 基本上我唯一想要的就是在当前的“AppDomain”中搜索类型。但由于这不再可用,我想出了这种方法。有什么建议吗?
    • 大多数时候,您实际上并不想查看已加载的类型。你到底想做什么?
    • @davidfowl 我正在构建一个模块化应用程序,我需要根据存储在数据库中的全名查找类型。我目前的方法是遍历所有加载的程序集及其类型并寻找匹配项。我还使用它来查找(开放通用)接口的实现。
    • @HenkMollema new AssemblyName() 刚刚拯救了我的夜晚 :) 谢谢
    【解决方案4】:

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

    在您的类头中,您需要声明 using 类似于:

    #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
    

    【讨论】:

      【解决方案5】:

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

              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;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-11-16
        • 1970-01-01
        • 2016-10-24
        • 1970-01-01
        • 1970-01-01
        • 2011-12-29
        • 1970-01-01
        相关资源
        最近更新 更多