.Net Core 2.0中Controller和BaseController中的dependency injection重复

如果我在我的Asp.Net Core 2.0 Web应用程序中创建一个BaseController,其中包含一些常见的依赖关系,它们仍然是实际控制器中必需的。

例如,默认MVC 6 Web应用程序中的标准Account和Manage控制器。

public class AccountController : Controller { private readonly UserManager _userManager; private readonly SignInManager _signInManager; private readonly IEmailSender _emailSender; private readonly ILogger _logger; public AccountController( UserManager userManager, SignInManager signInManager, IEmailSender emailSender, ILogger logger) { _userManager = userManager; _signInManager = signInManager; _emailSender = emailSender; _logger = logger; } //rest of code removed } public class ManageController : Controller { private readonly UserManager _userManager; private readonly SignInManager _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 userManager, SignInManager signInManager, IEmailSender emailSender, ILogger logger, UrlEncoder urlEncoder) { _userManager = userManager; _signInManager = signInManager; _emailSender = emailSender; _logger = logger; _urlEncoder = urlEncoder; } // rest of code removed } 

在我构建的自定义Web应用程序模板中,我将帐户控制器重构为三个不同的控制器,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 _signInManager; private readonly UserManager _userManager; protected BaseController(IConfiguration iconfiguration, UserManager userManager, SignInManager signInManager, IEmailSender emailSender, ILogger logger) { _config = iconfiguration; _userManager = userManager; _signInManager = signInManager; _emailSender = emailSender; _logger = logger; } //rest of code removed } 

但似乎没有完成任何事情? 因为在我看来,我仍然需要注入一切。 我不能正确(我是DI的新手,所以显然没有任何线索)但是BaseController应该允许我在BaseController和RegisterController之间执行NO DI。 我错了吗? 我如何完成我想要做的事情?

 public class RegisterController : BaseController { private const string ConfirmedRegistration = "User created a new account with password."; private readonly UserManager _userManager; private readonly SignInManager _signInManager; private readonly IEmailSender _emailSender; private readonly ILogger _logger; private readonly IConfiguration _config; public RegisterController( IConfiguration config, UserManager userManager, SignInManager signInManager, IEmailSender emailSender, ILogger logger) : base(config, userManager, signInManager, emailSender, logger) { _userManager = userManager; _signInManager = signInManager; _emailSender = emailSender; _logger = logger; _config = config; } //rest of code removed } 

更新

根据Rufo爵士的建议

 public abstract class BaseController : Controller { protected UserManager UserManager { get; } protected SignInManager SignInManager { get; } protected IConfiguration Config { get; } protected IEmailSender EmailSender { get; } protected ILogger AppLogger { get; } protected BaseController(IConfiguration iconfiguration, UserManager userManager, SignInManager signInManager, IEmailSender emailSender, ILogger logger) { AppLogger = logger; EmailSender = emailSender; Config = iconfiguration; SignInManager = signInManager; UserManager = userManager; } } 

和inheritance控制器

 public class TestBaseController : BaseController { public TestBaseController() : base() { } } 

这不起作用。 Resharper告诉我,我必须在TestBaseController构造函数中将参数添加到基础构造函数调用中。

BaseController还应该inheritance自.Net Core 2.0中的Controller或ControllerBase吗?

在MVC中使用BaseController原因很少 。 此方案中的基本控制器仅添加更多代码进行维护,没有任何实际好处。

对于真正的跨领域问题 ,在MVC中处理它们的最常用方法是使用全局filter ,尽管在MVC核心中有一些值得考虑的新选项。

但是,您的问题看起来不像是违反单一责任原则的跨领域问题。 也就是说,拥有超过3个注入的依赖项是一个代码气味,你的控制器做得太多了。 最实际的解决方案是重构服务 。

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

一些迹象表明控制器做得太多了:

  1. 有许多“辅助”方法包含在操作之间共享的业务逻辑。
  2. Action方法不仅仅是简单的HTTP请求/响应。 动作方法通常应该只执行处理输入和/或生成输出和返回视图和响应代码的服务。

在这些情况下,值得一看,您是否可以将该逻辑移动到自己的服务中,将任何共享逻辑移动到该服务的依赖关系中等等。

Microsoft.AspNetCore.MVC.Controller类附带扩展方法

HttpContext.RequestServices.GetService

只要HttpContext在管道中可用,就可以使用它(例如,如果从控制器的构造函数调用,HttpContext属性将为Null)

尝试这种模式

注意:确保使用Microsoft.Extensions.DependencyInjection包含此指令;

基础控制器

 public abstract class BaseController : Controller where T: BaseController { private ILogger _logger; protected ILogger Logger => _logger ?? (_logger = HttpContext.RequestServices.GetService>()); 

儿童控制器

 [Route("api/authors")] public class AuthorsController : BaseController { public AuthorsController(IAuthorRepository authorRepository) { _authorRepository = authorRepository; } [HttpGet("LogMessage")] public IActionResult LogMessage(string message) { Logger.LogInformation(message); return Ok($"The following message has been logged: '{message}'"); } 

不用说,请记住在Startup.cs – > ConfingureServices方法中注册您的服务

根据Calc和Sir Rufo的建议,这是有效的。

  public abstract class BaseController : Controller { protected UserManager UserManager { get; } protected SignInManager SignInManager { get; } protected IConfiguration Config { get; } protected IEmailSender EmailSender { get; } protected ILogger AppLogger { get; } protected BaseController(IConfiguration iconfiguration, UserManager userManager, SignInManager signInManager, IEmailSender emailSender, ILogger logger) { AppLogger = logger; EmailSender = emailSender; Config = iconfiguration; SignInManager = signInManager; UserManager = userManager; } protected BaseController() { } } 

参数仍然必须注入到inheritance的控制器中并传递给基础构造函数

 public class TestBaseController : BaseController { public static IConfigurationRoot Configuration { get; set; } public TestBaseController(IConfiguration config, UserManager userManager, SignInManager signInManager, IEmailSender emailSender, ILogger logger) : base(config,userManager,signInManager,emailSender,logger) { } public string TestConfigGetter() { var t = Config["ConnectionStrings:DefaultConnection"]; return t; } public class TestViewModel { public string ConnString { get; set; } } public IActionResult Index() { var tm = new TestViewModel { ConnString = TestConfigGetter() }; return View(tm); } } 

所以现在所有注入的对象都有实例。

希望最终的解决方案不需要将常用的实例注入每个inheritance的控制器,只需要该特定控制器所需的任何其他实例对象。 我从代码重复方面真正解决的是删除每个Controller中的私有字段。

仍然想知道BaseController是否应inheritance自Controller或ControllerBase?