繁体   English   中英

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

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

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

可以理解的是,抛出'BaseController' does not contain a constructor that takes 0 arguments的错误'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;
    }
}

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

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

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

    public BaseController() { }
}

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

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

谢谢!

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

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

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

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

顺便说一句,正确的术语是Subclass和Superclass。 “child”是子类,父类是“超类”。

使用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

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

SomeMember = serviceProvider.GetRequiredService<ISomeMember>();

我采取了一些不同的方法(但对我来说,相当明显,可能很常见)。 它适用于我,但可能存在我不知道的陷阱。

我在我的BaseController类中创建了大多数控制器使用的公共服务属性。 它们在需要/引用时被实例化。

如果某个特定控制器需要的服务使用较少,我会照常将其注入该控制器的构造函数中。

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

在我的控制器中,我只是从这个BaseController继承:

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

通过这种方式,可以在需要时动态生成常用服务,并且可以在没有大量样板的情况下使用我的控制器。

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM