创建实例并解决依赖关系

时间:2018-09-13 09:42:33

标签: c# .net reflection dependency-injection inversion-of-control

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

  1. 获取主项目程序集;
  2. 使用System.Reflection检索所有类型;
  3. 应该使用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

2 个答案:

答案 0 :(得分:1)

只要已解析的库在同一文件夹下(或在GAC中),就可以自动解决依赖性。 如果这些库位于特定文件夹下,则自动解析可能会失败,但是您可以使用AppDomain.AssemblyResolve事件来处理它。

此外,似乎您正在尝试实现某种插件/附加程序主机,也许您可​​以尝试使用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);
}

答案 1 :(得分:0)

我找到了一种解决方法。

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