【问题标题】:How to dynamically load assemblies in dotnet core如何在 dotnet core 中动态加载程序集
【发布时间】:2017-04-11 18:49:55
【问题描述】:

我正在构建一个 Web 应用程序,我希望在其中有单独的关注点,即在不同的项目中具有抽象和实现。

为了实现这一点,我尝试实现一个组合根概念,其中所有实现都必须有一个 ICompositionRootComposer 的实例来注册服务、类型等。

public interface ICompositionRootComposer
{
    void Compose(ICompositionConfigurator configurator);
}

在构建层次结构中直接引用的项目中,ICompositionRootComposer 的实现被调用,并且服务在底层 IoC 容器中正确注册。

当我尝试在项目中注册服务时出现问题,我在其中设置了一个构建后任务,将构建的 dll 复制到 Web 项目的调试文件夹:

cp -R $(TargetDir)"assembly and symbol name"* $(SolutionDir)src/"webproject path"/bin/Debug/netcoreapp1.1

我正在加载程序集:(灵感:How to load assemblies located in a folder in .net core console app

internal class AssemblyLoader : AssemblyLoadContext
{
    private string folderPath;

    internal AssemblyLoader(string folderPath)
    {
        this.folderPath = Path.GetDirectoryName(folderPath);
    }

    internal Assembly Load(string filePath)
    {
        FileInfo fileInfo = new FileInfo(filePath);
        AssemblyName assemblyName = new AssemblyName(fileInfo.Name.Replace(fileInfo.Extension, string.Empty));

        return this.Load(assemblyName);
    }

    protected override Assembly Load(AssemblyName assemblyName)
    {
        var dependencyContext = DependencyContext.Default;
        var ressource = dependencyContext.CompileLibraries.FirstOrDefault(r => r.Name.Contains(assemblyName.Name));

        if(ressource != null)
        {
            return Assembly.Load(new AssemblyName(ressource.Name));
        }

        var fileInfo = this.LoadFileInfo(assemblyName.Name);
        if(File.Exists(fileInfo.FullName))
        {
            Assembly assembly = null;
            if(this.TryGetAssemblyFromAssemblyName(assemblyName, out assembly))
            {
                return assembly;
            }
            return this.LoadFromAssemblyPath(fileInfo.FullName);
        }

        return Assembly.Load(assemblyName);
    }

    private FileInfo LoadFileInfo(string assemblyName)
    {
        string fullPath = Path.Combine(this.folderPath, $"{assemblyName}.dll");

        return new FileInfo(fullPath);
    }

    private bool TryGetAssemblyFromAssemblyName(AssemblyName assemblyName, out Assembly assembly)
    {
        try
        {
            assembly = Default.LoadFromAssemblyName(assemblyName);
            return true;
        }
        catch
        {
            assembly = null;
            return false;
        }
    }
}

有了这个,我可以加载程序集并调用项目ICompositionRootComposer 实现。

但问题是它似乎无法识别我的任何类型。

当调用我的配置器时

configurator.RegisterTransiantService<IFoo, Foo>();

它应该在 IoC 中注册 IFoo 和 Foo。
但是在调试时,我无法获取类型的信息,即通过 Visual Studio Code 的调试控制台中的 typeof(Foo)。

【问题讨论】:

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


    【解决方案1】:

    亡灵术。


    您可以为旧的Assembly.LoadFile 创建一个包装类来执行此操作。

    这还有一个额外的好处,即您可以通过在旧代码库中应用搜索和替换更改来保持与 dotnet-none-core 的向后兼容。

    namespace System.Reflection
    {
        public class Assembly2
        {
            public static System.Reflection.Assembly LoadFile(string path)
            {
                System.Reflection.Assembly assembly = null;
                
    #if NET_CORE
                // Requires nuget - System.Runtime.Loader
                assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
    #else
                assembly = System.Reflection. Assembly.LoadFile(path);
    #endif 
                return assembly;
            }
        }
    }
    

    您需要通过 NuGet 添加System.Runtime.Loader

    【讨论】:

    • 我实际上最终连接到 AssemblyLoadContext.Resolving 事件以帮助加载上下文加载它无法加载自身的程序集。
    【解决方案2】:

    我找到了解决问题的方法。

    事实证明,我将属性 PreserveCompilationContext 设置为 true,这就是调试器不会注册我手动复制的程序集的原因。 当我从 web 项目 csproj 文件中删除该属性时,一切正常。

    【讨论】:

      【解决方案3】:

      您是否知道 ASP.NET Core 有它自己的内置 Dependency Injection 机制?它可以根据您的需要轻松切换到其他 IoC 容器,我认为您不需要重新发明它。

      您需要在这里做的是使用反射来创建一个通用方法并在此之后调用,如下所示:

      public void ConfigureServices(IServiceCollection services)
      {
          var myAssembly = LoadAssembly();
          // find first interface
          var firstInterfaceType = myAssembly.DefinedTypes.FirstOrDefault(t => t.IsInterface).GetType();
          // find it's implementation
          var firstInterfaceImplementationType = myAssembly.DefinedTypes.Where(t => t.ImplementedInterfaces.Contains(firstInterfaceType)).GetType();
      
          // get general method info
          MethodInfo method = typeof(IServiceCollection).GetMethod("AddTransient");
          // provide types to generic method
          MethodInfo generic = method.MakeGenericMethod(firstInterfaceType, firstInterfaceImplementationType);
          // register your types
          generic.Invoke(services, null);
      }
      

      【讨论】:

      • 我是.net core DI机制的奖项,但我想保留项目中实现的注册,代码所在的地方。您的方法似乎保持了与 Web 项目的外部项目的硬耦合。
      • ConfigureService 是 ASP.NET 中依赖注入的组合根。您可以在自己实现的根目录中使用相同的方法
      • 我仍然不相信 - 不管怎样,我认为我们有点跑题了。我问为什么我复制的项目中的类型无法加载,即使我可以在同一个项目中调试我的作曲家。
      • 正如我所说,您可以在自己的代码中使用相同的方法。你试过了吗?
      猜你喜欢
      • 1970-01-01
      • 2017-11-16
      • 2020-06-16
      • 2017-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-07
      • 2016-10-24
      相关资源
      最近更新 更多