![](/img/trans.png)
[英]net core 2.0 xunit Controller with dependency injection using Mock is null
[英]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個注入的依賴項是一個代碼氣味,你的控制器做得太多了。 最實際的解決方案是重構服務 。
在這種情況下,我認為你至少有一個隱式服務需要明確 - 即, UserManager
和SignInManager
應該包裝到它自己的服務中。 從那里,您可以將其他3個依賴項注入該服務(當然,取決於它們的使用方式)。 因此,您可能會將此減少為AccountController
和ManageController
的單個依賴ManageController
。
一些跡象表明控制器做得太多了:
在這些情況下,值得一看,您是否可以將該邏輯轉移到自己的服務中,將任何共享邏輯轉移到該服務的依賴關系中等等。
根據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.