简体   繁体   English

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

[英]Base controller constructor injection in ASP.NET MVC with Unity

I have a base controller in my MVC 5 project which implements some shared functionality. 我的MVC 5项目中有一个基本控制器,它实现了一些共享功能。 This functionality requires some dependencies. 此功能需要一些依赖项。 I am using Unity 3 to inject these implementations into my controllers, a pattern which has worked fine until I switched my controllers to inherit from this base controller. 我正在使用Unity 3将这些实现注入到我的控制器中,这种模式运行良好,直到我将控制器切换为从该基本控制器继承。 Now I am running into the following issue: 现在我遇到了以下问题:

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

This is understandably throwing an error of 'BaseController' does not contain a constructor that takes 0 arguments . 可以理解的是,抛出'BaseController' does not contain a constructor that takes 0 arguments的错误'BaseController' does not contain a constructor that takes 0 arguments Unity is not resolving the construction of the BaseController, so it can't inject the dependencies into it. Unity没有解析BaseController的构造,因此它无法将依赖项注入其中。 I see two obvious ways of solving this issue: 我看到了解决这个问题的两种明显方法:

1.) Explicitly call the BaseController ctor and have each ChildController ctor inject the BaseController's dependencies 1.)显式调用BaseController ctor并让每个ChildController ctor注入BaseController的依赖项

public class ChildController : BaseController
{
    private readonly IDifferentService _differentService;

    public ChildController(IDifferentService differentService,
                           IService baseService)
        : base(baseService)
    {
        _differentService = differentService;
    }
}

I don't like this approach for several reasons: one, because the ChildControllers are not making use of the additional dependencies (so it causes constructor bloat in the child controllers for no reason), and more importantly, if I ever change the constructor signature of the Base Controller, I have to change the constructor signatures of each child controller. 我不喜欢这种方法有几个原因:一,因为ChildControllers没有使用额外的依赖项(因此它会导致子控制器中的构造函数膨胀),更重要的是,如果我更改了构造函数签名基本控制器,我必须更改每个子控制器的构造函数签名。

2.) Implement the BaseController's dependencies via property injection 2.)通过属性注入实现BaseController的依赖项

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

    public BaseController() { }
}

I like this approach better - I'm not using any of the dependencies in the BaseController's constructor code - but it makes the dependency injection strategy of the code inconsistent, which isn't ideal either. 我更喜欢这种方法 - 我没有使用BaseController的构造函数代码中的任何依赖项 - 但它使代码的依赖注入策略不一致,这也不理想。

There's probably an even better approach which involves some sort of BaseController dependency resolution function that calls on the Unity container to sort out the ctor's method signature, but before I get into writing anything too involved, I was wondering if anyone had solved this problem before? 可能有一种更好的方法,它涉及某种类型的BaseController依赖解析函数,该函数调用Unity容器来整理ctor的方法签名,但在我开始编写任何涉及的内容之前,我想知道是否有人之前已经解决了这个问题? I found a few solutions floating around on the web, but they were workarounds such as Service Locator which I don't want to use. 我发现网上有一些解决方案,但它们是我不想使用的服务定位器等解决方法。

Thanks! 谢谢!

The first thing you have to understand is that you aren't instantiating the base controller. 您必须要了解的第一件事是您没有实例化基本控制器。 You're instantiating the child controller, which inherits the base controllers interface and functionality. 您正在实例化子控制器,它继承了基本控制器的接口和功能。 This is an important distinction. 这是一个重要的区别。 When you say "the ChildControllers are not making use of the additional dependencies", then you're absolutely wrong. 当你说“ChildControllers没有使用其他依赖项”时,你就完全错了。 Because the ChildController IS the BaseController as well. 由于ChildController IS的BaseController为好。 There aren't two different classes created. 没有创建两个不同的类。 Just one class that implements both functionality. 只有一个实现这两种功能的类。

So, since ChildController IS A BaseController, there is nothing wrong or strange about passing parameters in the child controllers constructor that calls the base classes constructor. 因此,由于ChildController 是一个 BaseController,因此在调用基类构造函数的子控制器构造函数中传递参数没有任何错误或奇怪。 This is the way it should be done. 这是它应该做的方式。

If you change your base class, you will likely have to change your child classes anyways. 如果更改基类,则可能必须更改子类。 There is no way to use constructor injection to inject base class dependencies that are not included in the child class. 无法使用构造函数注入来注入未包含在子类中的基类依赖项。

I do not recommend property injection, since this means your objects can be created without proper initialization, and you have to remember to configure them correctly. 我不建议使用属性注入,因为这意味着可以在没有正确初始化的情况下创建对象,并且必须记住正确配置它们。

BTW, the proper terms are Subclass and Superclass. 顺便说一句,正确的术语是Subclass和Superclass。 A "child" is a subclass, the parent is the "superclass". “child”是子类,父类是“超类”。

With ASP.Net 5 and it's built in DI 使用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)
{}
}

UPDATE UPDATE

There is also an extension method in Microsoft.Extensions.DependencyInjection to make it shorter Microsoft.Extensions.DependencyInjection中还有一个扩展方法,可以缩短它

SomeMember = serviceProvider.GetRequiredService<ISomeMember>();

I've taken a somewhat different (but to me, fairly obvious and probably common) approach. 我采取了一些不同的方法(但对我来说,相当明显,可能很常见)。 It works for me, but there may be pitfalls I'm not aware of. 它适用于我,但可能存在我不知道的陷阱。

I have created common service properties in my BaseController class that most of my controllers use. 我在我的BaseController类中创建了大多数控制器使用的公共服务属性。 They get instantiated when they are needed/referenced. 它们在需要/引用时被实例化。

If there's a service a particular controller needs that is less used, I inject it into that controller's constructor as normal. 如果某个特定控制器需要的服务使用较少,我会照常将其注入该控制器的构造函数中。

using JIS.Context;
using JIS.Managers;
using JIS.Models;
using JIS.Services;
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;

namespace JIS.Controllers
{
    public class BaseController : Controller
    {
        public BaseController()
        {
            ViewBag.User = UserManager?.User;
        }

        private IUserManager userManager;
        public IUserManager UserManager
        {
            get
            {
                if (userManager == null)
                {
                    userManager = DependencyResolver.Current.GetService<IUserManager>();
                }
                return userManager;
            }
            set
            {
                userManager = value;
            }
        }

        private ILoggingService loggingService;
        public ILoggingService LoggingService
        {
            get
            {
                if (loggingService == null)
                {
                    loggingService = DependencyResolver.Current.GetService<ILoggingService>();
                }
                return loggingService;
            }
            set { loggingService = value; }
        }

        private IUserDirectory userDirectory;
        public IUserDirectory UserDirectory
        {
            get
            {
                if (userDirectory == null)
                {
                    userDirectory = DependencyResolver.Current.GetService<IUserDirectory>();
                }
                return userDirectory;
            }
            set { userDirectory = value; }
        }

        private ApplicationDbContext appDb;
        public ApplicationDbContext AppDb
        {
            get
            {
                if (appDb == null)
                {
                    appDb = new ApplicationDbContext();
                }
                return appDb;
            }
            set
            {
                appDb = value;
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && appDb != null)
            {
                appDb.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

In my controller I simply subclass from this BaseController: 在我的控制器中,我只是从这个BaseController继承:

public class UsersController : BaseController
{
    // and any other services I need are here:
    private readonly IAnotherService svc;
    public UsersController(IAnotherService svc)
    {
        this.svc = svc;
    }
...
}

This way, common services are generated on the fly when they're needed, and are available to my controllers without a lot of boilerplate. 通过这种方式,可以在需要时动态生成常用服务,并且可以在没有大量样板的情况下使用我的控制器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 带有构造函数注入的单元测试ASP.NET MVC控制器 - Unit testing asp.net MVC controller with constructor injection ASP.Net MVC控制器注入 - ASP.Net MVC Controller injection 尝试解析控制器时,Asp.Net MVC Unity依赖项注入会引发异常 - Asp.Net MVC Unity dependency injection gives an exception when trying to resolve a controller Asp.Net Mvc控制器静态构造函数 - Asp.Net Mvc controller static constructor ASP.NET MVC 2中的基本控制器实现 - Base controller implementation in ASP.NET MVC 2 在ASP.NET MVC 4中使用Unity-依赖项注入错误 - Using Unity in ASP.NET MVC 4 - Dependency injection error 具有Autofac的ASP.Net MVC构造函数注入-运行时参数 - ASP.Net MVC Constructor Injection with Autofac - Runtime parameters asp.net mvc核心依赖注入构造函数参数 - asp.net mvc core dependency injection constructor parameter ASP.net MVC依赖注入与unity(DI容器); 如何根据不同的控制器类型注入依赖项别名(特定类型) - ASP.net MVC dependency injection with unity(DI container); how to inject dependency alias(specific types) based on the different controller types 从ASP.NET 5中的基本控制器构造函数访问身份用户 - Access Identity User from base controller constructor in ASP.NET 5
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM