簡體   English   中英

.Net Core 2.0中Controller和BaseController中的依賴注入重復

[英]Dependency injection duplication in Controller and BaseController in .Net Core 2.0

如果我在我的Asp.Net Core 2.0 Web應用程序中創建一個BaseController,其中包含一些常見的依賴關系,它們仍然是實際控制器中必需的。

例如,默認MVC 6 Web應用程序中的標准Account和Manage控制器。

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應用程序模板中,我將帳戶控制器重構為三個不同的控制器,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
}

但似乎沒有完成任何事情? 因為在我看來,我仍然需要注入一切。 我不能正確(我是DI的新手,所以顯然沒有任何線索)但是BaseController應該允許我在BaseController和RegisterController之間執行NO 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
}

更新

根據Rufo爵士的建議

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

和繼承控制器

public class TestBaseController : BaseController
{

    public TestBaseController() : base()
    {

    }
}

這不起作用。 Resharper告訴我,我必須在TestBaseController構造函數中將參數添加到基礎構造函數調用中。

BaseController也應該繼承自.Net Core 2.0中的Controller或ControllerBase嗎?

Microsoft.AspNetCore.MVC.Controller類附帶擴展方法

HttpContext.RequestServices.GetService<T>

只要HttpContext在管道中可用,就可以使用它(例如,如果從控制器的構造函數調用HttpContext屬性將為Null)

試試這種模式

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

基礎控制器

public abstract class BaseController<T> : Controller where T: BaseController<T>
{

    private ILogger<T> _logger;

    protected ILogger<T> Logger => _logger ?? (_logger = HttpContext.RequestServices.GetService<ILogger<T>>());

兒童控制器

[Route("api/authors")]
public class AuthorsController : BaseController<AuthorsController>
{

    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方法中注冊您的服務

在MVC中使用BaseController原因很少 此方案中的基本控制器僅添加更多代碼進行維護,沒有任何實際好處。

對於真正的跨領域問題 ,在MVC中處理它們的最常用方法是使用全局過濾器 ,盡管在MVC核心中有一些值得考慮的新選項。

但是,您的問題看起來不像是違反單一責任原則的跨領域問題。 也就是說,擁有超過3個注入的依賴項是一個代碼氣味,你的控制器做得太多了。 最實際的解決方案是重構服務

在這種情況下,我認為你至少有一個隱式服務需要明確 - 即, UserManagerSignInManager應該包裝到它自己的服務中。 從那里,您可以將其他3個依賴項注入該服務(當然,取決於它們的使用方式)。 因此,您可能會將此減少為AccountControllerManageController的單個依賴ManageController

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

  1. 有許多“輔助”方法包含在操作之間共享的業務邏輯。
  2. Action方法不僅僅是簡單的HTTP請求/響應。 動作方法通常應該只執行處理輸入和/或生成輸出和返回視圖和響應代碼的服務。

在這些情況下,值得一看,您是否可以將該邏輯轉移到自己的服務中,將任何共享邏輯轉移到該服務的依賴關系中等等。

根據Calc和Sir Rufo的建議,這是有效的。

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

    protected BaseController()
    {
    }
}

參數仍然必須注入到繼承的控制器中並傳遞給基礎構造函數

public class TestBaseController : BaseController
{
    public static IConfigurationRoot Configuration { get; set; }

    public TestBaseController(IConfiguration config,
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        IEmailSender emailSender,
        ILogger<ManageController> 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);
    }
}

所以現在所有注入的對象都有實例。

希望最終的解決方案不需要將常用的實例注入每個繼承的控制器,只需要該特定控制器所需的任何其他實例對象。 我從代碼重復方面真正解決的是刪除每個Controller中的私有字段。

仍然想知道BaseController是否應該繼承自Controller或ControllerBase?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM