ASP.NET核心MVC中的动态依赖项注入

时间:2018-09-25 03:29:31

标签: c# .net dependency-injection asp.net-core-2.0

在我的应用中,有一个接口和两个子类。 两个子类已注册为服务。 我的问题是当我使用它们时如何控制获得哪一个?

HomeController.cs

public class HomeController : Controller
{
    public interface ITestDI
    {

    }

    public class TestDIClass1: ITestDI
    {
        public TestDIClass1()
        {

        }
    }

    public class TestDIClass2 : ITestDI
    {
        public TestDIClass2()
        {

        }
    }

    ITestDI td;

    public HomeController(ITestDI _td)
    {
        this.td = _td; // how to control which ITestDI implementation will injected with constructor injection? With the configuration below, always get TestDIClass2.
    }

    public IActionResult Index()
    {
        return View();
    }
} 

Startup.cs

services.AddScoped<ITestDI, TestDIClass1>();
services.AddScoped<ITestDI, TestDIClass2>(); // it seems like TestDIClass2 has overwrited the TestDIClass1.

3 个答案:

答案 0 :(得分:1)

有一些选择。

首先,您可以放弃自动布线并使用委托进行注册,如下所示:

services.AddScoped<TestDIClass1>();    
services.AddScoped<TestDIClass2>();

services.AddTransient<HomeController>(c => new HomeController(
    _td: c.GetRequiredService<TestDIClass2>());   

在这里,您要按其具体类型注册两个ITestDI。这允许HomeController的委托人使用GetRequiredService<T>请求特定的实现。

但是,

手动连接此类对象会变得很麻烦,尤其是在HomeController包含更多依赖项的情况下,因为这将需要您手动解析所有 项依赖项。因此,您可以使用MS.DI的ActivatorUtilities.CreateInstance类。它实现了MS.DI的自动装配功能的简化形式。以下代码片段显示了这一点:

services.AddScoped<TestDIClass1>();    
services.AddScoped<TestDIClass2>();

services.AddTransient<HomeController>(c =>
    ActivatorUtilities.CreateInstance<HomeController>(
        c,                                             
        new object[]                                   
        {                                              
            c.GetRequiredService<TestDIClass2>(),         
        }));

在此示例中,从HomeController请求了ActivatorUtilities。向CreateInstance调用提供了一个IServiceProvider实例(c参数)和一个实例数组,以注入到HomeController的构造函数中。 ActivatorUtilities将提供的对象与HomeController的构造函数参数匹配,并将解析提供的IServiceProvider中所有缺少的参数。

使用ActivatorUtilities.CreateInstance可使您的注册保持不变,即使将新参数添加到HomeController的构造函数中,例如:

public HomeController(
    ILogger logger,          // new argument added
    ITestDI td,
    IProductService service  // another argument added
) 

ActivatorUtilities.CreateInstance将指出,已解析的TestDIClass2可映射到td构造函数参数,它将从{解析ILoggerIProductService {1}}。

还有其他可用选项,但是如果您不想更改设计,这些可能是您最有可能的选项。

此信息是尚未发行的书籍Dependency Injection in .NET, 2nd edition的精简版。其中包含有关Microsoft.Extensions.DependencyInjection(MS.DI)的完整章节,以及有关使用多个组件的大约16页。该章的预发行版本已经可以下载。

答案 1 :(得分:0)

可以注册具体课程。

public HomeController(TestDIClass2 _td)
{
    this.td = _td; //
}

答案 2 :(得分:0)

由于您已经注册了多个实例,因此必须向Dependency Injection框架索取IEnumerable<ITestDI> _testDis;

然后您必须对其进行迭代

foreach(var instance in _testDis)
{
    instance.YourMethod();
}