使用Unity的ASP.NET MVC中的基本控制器构造函数注入

NWa*_*ard 27 c# asp.net-mvc dependency-injection inversion-of-control unity-container

我的MVC 5项目中有一个基本控制器,它实现了一些共享功能.此功能需要一些依赖项.我正在使用Unity 3将这些实现注入到我的控制器中,这种模式运行良好,直到我将控制器切换为从该基本控制器继承.现在我遇到了以下问题:

public class BaseController : Controller
{
    private readonly IService _service;

    public BaseController(IService service)
    {
        _service = service;
    }
}

public class ChildController : BaseController
{
    private readonly IDifferentService _differentService;

    public ChildController(IDifferentService differentService)
    {
        _differentService = differentService;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是可以理解的抛出错误'BaseController' does not contain a constructor that takes 0 arguments.Unity没有解析BaseController的构造,因此它无法将依赖项注入其中.我看到了解决这个问题的两种明显方法:

1.)显式调用BaseController ctor并让每个ChildController ctor注入BaseController的依赖项

public class ChildController : BaseController
{
    private readonly IDifferentService _differentService;

    public ChildController(IDifferentService differentService,
                           IService baseService)
        : base(baseService)
    {
        _differentService = differentService;
    }
}
Run Code Online (Sandbox Code Playgroud)

我不喜欢这种方法有几个原因:一,因为ChildControllers没有使用额外的依赖项(因此它会导致子控制器中的构造函数膨胀),更重要的是,如果我更改了构造函数签名基本控制器,我必须更改每个子控制器的构造函数签名.

2.)通过属性注入实现BaseController的依赖项

public class BaseController : Controller
{
    [Dependency]
    public IService Service { get; set; }

    public BaseController() { }
}
Run Code Online (Sandbox Code Playgroud)

我更喜欢这种方法 - 我没有使用BaseController的构造函数代码中的任何依赖项 - 但它使代码的依赖注入策略不一致,这也不理想.

可能有一种更好的方法,它涉及某种类型的BaseController依赖解析函数,该函数调用Unity容器来整理ctor的方法签名,但在我开始编写任何涉及的内容之前,我想知道是否有人之前已经解决了这个问题?我发现网上有一些解决方案,但它们是我不想使用的服务定位器等解决方法.

谢谢!

Eri*_*sch 29

您必须要了解的第一件事是您没有实例化基本控制器.您正在实例化子控制器,它继承了基本控制器的接口和功能.这是一个重要的区别.当你说"ChildControllers没有使用其他依赖项"时,你就完全错了.由于ChildController IS的BaseController为好.没有创建两个不同的类.只有一个实现这两种功能的类.

因此,由于ChildController 是一个 BaseController,因此在调用基类构造函数的子控制器构造函数中传递参数没有任何错误或奇怪.这是它应该做的方式.

如果更改基类,则可能必须更改子类.无法使用构造函数注入来注入未包含在子类中的基类依赖项.

我不建议使用属性注入,因为这意味着可以在没有正确初始化的情况下创建对象,并且必须记住正确配置它们.

顺便说一下,正确的术语是Subclass和Superclass."child"是子类,父类是"超类".

  • @JonathanWood - 你所谓的"方法"就是我所知道的所有面向对象语言的工作方式,而且由于这是......定义为"面向对象的方法",我不太明白你的意思.据我所知,所有实现继承的面向对象语言都是以派生类是对象的唯一实例的方式实现的,因此没有像Unity或任何其他DI容器这样的东西可以注入值的替代实例.如果派生类本身没有实现该构造函数. (2认同)

Vit*_*aly 13

使用ASP.Net 5,它内置DI

public class BaseController : Controller
{
    protected ISomeType SomeMember { get; set; }

    public BaseController(IServiceProvider serviceProvider)
    {
        //Init all properties you need
        SomeMember = (SomeMember)serviceProvider.GetService(typeof(ISomeMember));
    }
}

public class MyController : BaseController  
{
public MyController(/*Any other injections goes here*/, 
                      IServiceProvider serviceProvider) : 
 base(serviceProvider)
{}
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

Microsoft.Extensions.DependencyInjection中还有一个扩展方法,可以缩短它

SomeMember = serviceProvider.GetRequiredService<ISomeMember>();
Run Code Online (Sandbox Code Playgroud)