创建实例并解析依赖

Create instance and resolve dependencies

我有在主项目中引用的 c# 库。图书馆

  1. 获取主项目程序集;
  2. 使用 System.Reflection;
  3. 检索所有类型
  4. 应该使用 Activator.CreateInstance 创建类型(我不确定这是最好的方法)。

库对主项目一无所知,只有一个可以通过反射获取的元数据。 如何解决依赖关系?

private readonly Assembly _assembly;

public Injector()
{
    _assembly = Assembly.GetEntryAssembly();
}

public List<string> GetTypes()
{
    return _assembly
        .GetTypes()
        .Select(x => x.FullName)
        .ToList();
}

public object GetType(string typeName)
{
    Type type = _assembly
        .GetTypes()
        .First(x => x.FullName == typeName);

    object instance = Activator.CreateInstance(type);

    return instance;
}

可能的问题:不同的IoC容器(第三方库,自己写的)。

在不强迫用户提供大量设置的情况下,处理此问题并使库更加自动化的最佳方法是什么?如果不可能,您能否提供任何其他解决方案?谢谢。

编辑: 如何为 Activator.CreateInstance 中的实例提供依赖项或直接从主(源)项目创建实例?应该允许创建包含在主项目中的任何实例。是的,主项目也对库一无所知。因此,最好在主项目中进行最少的代码更改。

编辑 2: 该库不会在源项目中使用,它将有自己的 UI 接口。例如,Swagger API

只要解析的库在同一文件夹下(或在 GAC 中),依赖关系就会自动解析 如果库在特定文件夹下,自动解析可能会失败,但您可以通过 AppDomain.AssemblyResolve 事件处理。

此外,您似乎正在尝试实现一种 plugin/addon 主机,也许您可​​以尝试使用 Managed Extensibility Framework 而不是通过反射手动实现解决方案。

编辑: 以下是事件使用的代码片段,但需要根据您的环境进行调整

static Injector()
{
     // Usage of static constructor because we need a unique static handler
     // But feel free to move this part to a more global location
     AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
  string foundAssemblyPath = string.Empty;

  // args.Name contains name of the missing assembly
  // some project-specific and environment-specific code should be added here to manually resolve the dependant library
  // In this example I will consider that the libraries are located under /plugins folder
  foundAssemblyPath = $@"{Path.GetDirectoryName(Application.StartupPath)}\plugins\{args.Name}.dll";

  return Assembly.LoadFile(foundAssemblyPath);
}

我找到了处理这个问题的方法。

.NET Standard 具有一个新接口 IServiceProvider,必须由 Startup.ConfigureServices() 中的任何 IoC 容器实现。我们可以将此 IServiceProvider 传递给库构造函数并使用方法 GetService(Type type) 来创建具有所有解析的依赖项注入的服务。

源项目(例如 Startup.cs 中的 Autofac 容器):

    // This method gets called by the runtime. Use this method to add services to the container.
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        var builder = new ContainerBuilder();
        builder.RegisterType<CalculationService>().As<ICalculationService>();
        builder.RegisterType<Logger>().As<ILogger>();
        builder.Populate(services);
        var container = builder.Build();
        return new AutofacServiceProvider(container);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider diService)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseMvc();

        var injection = new Injector(diService);
        var result = injection.RunMethod(typeof(ICalculationService).FullName, "Sum", new int[] { 1, 2, 3 });
    }

图书馆项目:

public class Injector
{
    private readonly Assembly _assembly;
    private readonly IServiceProvider _serviceProvider;

    public Injector(IServiceProvider serviceProvider)
    {
        _assembly = Assembly.GetEntryAssembly();
        _serviceProvider = serviceProvider;
    }

    public object RunMethod(string className, string methodName, params object[] parameters)
    {
        var classType = _assembly
            .GetType(className);

        object instance = _serviceProvider.GetService(classType);

        var method = classType.GetMethod(methodName);

        var result = method.Invoke(instance, parameters);

        return result;
    }
}