C# 如何在dotnetcore中动态加载程序集

C# 如何在dotnetcore中动态加载程序集,c#,asp.net-core,.net-core,C#,Asp.net Core,.net Core,我正在构建一个web应用程序,在这里我需要单独的关注点,即在不同的项目中进行抽象和实现 为了实现这一点,我尝试实现一个compositionroot概念,其中所有实现都必须有一个ICompositionRootComposer实例来注册服务、类型等 public interface ICompositionRootComposer { void Compose(ICompositionConfigurator configurator); } 在构建层次结构中直接引用的项目中,将调用I

我正在构建一个web应用程序,在这里我需要单独的关注点,即在不同的项目中进行抽象和实现

为了实现这一点,我尝试实现一个compositionroot概念,其中所有实现都必须有一个
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
我正在加载程序集:(灵感:)

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

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

使用调用我的配置程序时

configurator.RegisterTransiantService<IFoo, Foo>();
configurator.RegisterTransiantService();
它应该在国际奥委会注册IFoo和Foo。

但是在调试时,我无法获得类型的信息,即通过Visual Studio代码中调试控制台中的typeof(Foo)获取信息。

您知道ASP.NET Core有自己的内置机制吗?它可以根据您的需要轻松地切换到其他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);
}

我找到了解决问题的办法

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

亡灵巫术

您可以为旧的Assembly.LoadFile创建一个包装器类来完成此操作
这还有一个额外的好处,即通过在旧代码库中应用搜索和替换更改,您可以保持与dotnet none core的向后兼容
您需要通过nuget添加
System.Runtime.Loader

namespace System.Reflection
{

    public class Assembly2
    {


        public static System.Reflection.Assembly LoadFile(string path)
        {
            System.Reflection.Assembly ass = null;

#if NET_CORE
            // Requires nuget - System.Runtime.Loader
            ass = System.Runtime.Loader.AssemblyLoadContext.Default
                   .LoadFromAssemblyPath(path);
#else
            ass = System.Reflection. Assembly.LoadFile(path);
#endif 
            // System.Type myType = ass.GetType("Custom.Thing.SampleClass");
            // object myInstance = Activator.CreateInstance(myType);
            return ass;
        }

    }

}

我很高兴获得.net核心DI机制的奖励,但我希望保留项目中实现的注册,代码放在项目中的位置。您的方法似乎保持了与web项目外部项目的硬耦合。
ConfigureService
是ASP.NET中依赖项注入的合成根。您可以在自己实现的roots中使用相同的方法,但我仍然不确信——不管怎样,我认为我们已经偏离了主题。我问为什么我复制的项目中的类型无法加载,即使我可以在同一项目中调试我的编写器。正如我所说,您可以在自己的代码中使用相同的方法。你试过了吗?我实际上连接到了
AssemblyLoadContext.Resolving
事件,以帮助加载上下文加载它自己无法加载的程序集。
namespace System.Reflection
{

    public class Assembly2
    {


        public static System.Reflection.Assembly LoadFile(string path)
        {
            System.Reflection.Assembly ass = null;

#if NET_CORE
            // Requires nuget - System.Runtime.Loader
            ass = System.Runtime.Loader.AssemblyLoadContext.Default
                   .LoadFromAssemblyPath(path);
#else
            ass = System.Reflection. Assembly.LoadFile(path);
#endif 
            // System.Type myType = ass.GetType("Custom.Thing.SampleClass");
            // object myInstance = Activator.CreateInstance(myType);
            return ass;
        }

    }

}