如果我在Asp.Net Core 2.0 Web应用程序中创建一个BaseController来封装一些常见的依赖关系,那么在实际的 Controller 中它们仍然是必需的。

例如,默认MVC 6 Web应用程序中的标准“帐户”和“管理” Controller 。

public class AccountController : Controller
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IEmailSender _emailSender;
    private readonly ILogger _logger;

    public AccountController(
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        IEmailSender emailSender,
        ILogger<AccountController> logger)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
        _logger = logger;
    }
   //rest of code removed
}

public class ManageController : Controller
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IEmailSender _emailSender;
    private readonly ILogger _logger;
    private readonly UrlEncoder _urlEncoder;

    private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";

    public ManageController(
      UserManager<ApplicationUser> userManager,
      SignInManager<ApplicationUser> signInManager,
      IEmailSender emailSender,
      ILogger<ManageController> logger,
      UrlEncoder urlEncoder)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
        _logger = logger;
        _urlEncoder = urlEncoder;
    }
    // rest of code removed
}

在我正在构建的定制Web应用程序模板中,我将帐户 Controller 重构为三个不同的 Controller ,RegisterController(负责处理与用户注册有关的所有内容),LoginController(处理登录和注销),以及余额中的三分之一。我将Manage Controller一分为二,一个ManagePasswordController(与密码有关的所有内容)和一个UserManageController(与其他所有内容有关的内容)。

每个DI要求都有很多共同点,我想将它们放在BaseController中。看起来像这样吗?
public abstract class BaseController : Controller
{
    private readonly IConfiguration _config;
    private readonly IEmailSender _emailSender;
    private readonly ILogger _logger;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly UserManager<ApplicationUser> _userManager;

     protected BaseController(IConfiguration iconfiguration,
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        IEmailSender emailSender,
        ILogger<ManageController> logger)
    {
        _config = iconfiguration;
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
        _logger = logger;
    }
    //rest of code removed
}

但这似乎无济于事?因为在我看来,我仍然必须注入(inject)一切。我不能说对(我是DI的新手,所以显然不知道),但是BaseController应该允许我做在BaseController和RegisterController之间不常见的DI。我错了吗?我该如何完成我想做的事情?
public class RegisterController : BaseController
{
    private const string ConfirmedRegistration = "User created a new account with password.";

    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IEmailSender _emailSender;
    private readonly ILogger _logger;
    private readonly IConfiguration _config;

     public RegisterController(
        IConfiguration config,
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        IEmailSender emailSender,
        ILogger<AccountController> logger) : base(config, userManager, signInManager, emailSender, logger)

    {
        _userManager = userManager;
        _signInManager = signInManager;
        _emailSender = emailSender;
        _logger = logger;
        _config = config;
    }
    //rest of code removed
}

更新

根据鲁弗爵士的建议
public abstract class BaseController : Controller
{
    protected UserManager<ApplicationUser> UserManager { get; }
    protected SignInManager<ApplicationUser> SignInManager { get; }
    protected IConfiguration Config { get; }
    protected IEmailSender EmailSender { get; }
    protected ILogger AppLogger { get; }

    protected BaseController(IConfiguration iconfiguration,
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        IEmailSender emailSender,
        ILogger<ManageController> logger)
    {
        AppLogger = logger;
        EmailSender = emailSender;
        Config = iconfiguration;
        SignInManager = signInManager;
        UserManager = userManager;
    }
}

和继承 Controller
public class TestBaseController : BaseController
{

    public TestBaseController() : base()
    {

    }
}

这行不通。 Resharper告诉我必须在TestBaseController构造函数的基本构造函数调用中添加参数。

还应该从.Net Core 2.0中的Controller或ControllerBase继承BaseController吗?

最佳答案

very few good reasons to use a BaseController in MVC。在这种情况下,基本 Controller 仅添加要维护的更多代码,没有任何实际好处。

对于真正的cross-cutting concerns,在MVC中处理它们的最常用方法是使用global filters,尽管MVC内核中有一些值得考虑的新选项。

但是,您的问题看起来像跨 Realm 的关注,而不是违反Single Responsibility Principle。也就是说,具有3个以上的注入(inject)依赖项是您的 Controller 做得太多的代码味道。最实用的解决方案是Refactor to Aggregate Services

在这种情况下,我认为您需要至少有1个需要显式的隐式服务-即UserManagerSignInManager应该包装成自己的服务。从那里,您可能会将其他3个依赖项注入(inject)到该服务中(当然,取决于它们的使用方式)。因此,您可能会将其降低为AccountControllerManageController的单个依赖项。

一些迹象表明 Controller 做得太多:

  • 有许多“帮助程序”方法,其中包含在操作之间共享的业务逻辑。
  • Action方法的作用不只是简单的HTTP请求/响应。 Action 方法通常不应执行任何操作,而应调用处理输入和/或产生输出并返回 View 和响应代码的服务。

  • 在这种情况下,值得一看的是,您是否可以将该逻辑移至其自己的服务中,并将任何共享逻辑移至该服务的依赖项中,等等。

    10-08 00:09