如何避免ASP .NET Core中的冗余DI代碼?
譯文【51CTO.com快譯】使用ASP.NET Core或ASP.NET Core MVC處理Web應用程序中的控制器時,您可能會遇到代碼冗余。比如說,可能遇到過使用依賴項注入(DI)來注入所需服務的控制器。如果注入依賴項的代碼在多個控制器中被重用,就存在代碼冗余,并違反DRY原則。
本文著重介紹DI代碼冗余,并介紹如何構建自定義基本控制器以避免此類問題。要使用本文中提供的代碼示例,應該在系統中安裝Visual Studio 2019。如果您還沒有安裝,可以在此處下載Visual Studio 2019。
Visual Studio中創建ASP.NET Core MVC項目
首先,不妨在Visual Studio 2019中創建一個ASP.NET Core項目。按照這些步驟將在 Visual Studio 2019中創建一個新的ASP.NET Core MVC項目。
1. 啟動Visual Studio IDE。
2. 點擊“創建新項目”。
3. 在“創建新項目”窗口中,從顯示的模板列表中選擇“ASP.NET Core Web App (Model-View-Controller)”。
4. 點擊“下一步”。
5. 在“配置新項目”窗口中,指定新項目的名稱和位置。
6. 根據個人喜好,選擇性勾選“將解決方案和項目放在同一目錄中”復選框。
7. 點擊“下一步”。
8. 在隨后顯示的“額外信息”窗口中,從頂部的下拉列表中選擇.NET 5.0作為目標框架。任由“身份驗證類型”處于“無”(默認)的狀態。
9. 確保“啟用Docker”、“為HTTPS配置”和“啟用Razor運行時編譯”等復選框未被勾選,因為我們不會在這里使用任何這些功能。
10. 點擊創建。
將創建一個新的ASP.NET Core MVC項目。我們將在本文的后續部分中使用這個項目來處理依賴項注入。
現在按照下面列出的步驟,在您的項目中創建額外控制器。
1. 鼠標右擊控制器解決方案文件夾。
2. 選擇添加 -> 控制器。
3. 在“添加新腳手架項”對話框中,選擇API作為模板(默認情況下將選擇MVC)。
4. 選擇“具有讀/寫操作的API控制器”這項。
5. 點擊添加。
6. 在隨后顯示的“添加新項”對話框中,為新控制器指定名稱。
7. 點擊添加。
ASP.NET Core中的內置基本控制器類
控制器有兩個基本類,即ControllerBase和Controller。ControllerBase類實現IController 接口,并提供幾個方法和屬性的實現。它定義了一個名為ExecuteCore的抽象方法,用于定位操作方法并執行它。無論何時構建API,您都應該使用ControllerBase。
Controller類擴展了ControllerBase類,提供了ExecuteCore方法,并添加了可以在控制器類中使用的幾個方法,比如View()和Redirect()。與ControllerBase一樣,Controller 類是支持視圖的基本控制器類。因此,只要在ASP.NET Core MVC中創建控制器,都應該使用Controller類。ControllerBase類提供了與路由和HttpContext的必要集成,以便您可以利用它們。它還含有管理ViewData和TempData所需的代碼。
ASP.NET Core中實現基本控制器類
我們在ASP.NET Core中創建新的API控制器類時,它默認擴展ControllerBase類。接下來,我們將創建基本控制器的實現。我們的基本控制器類將擴展框架的ControllerBase 類。
這是我們將在該示例中使用的實體類:
- public class Order
- {
- public int Id { get; set; }
- public int CustomerId { get; set; }
- public string Address { get; set; }
- }
現在創建一個名為IOrderManager的下列接口,含有ProcessOrder方法的聲明。
- public interface IOrderManager
- {
- public void ProcessOrder(Order order);
- }
接下來創建擴展IOrderManager接口,并實現ProcessOrder方法的OrderManager類。
- public class OrderManager : IOrderManager
- {
- public void ProcessOrder(Order order)
- {
- throw new System.NotImplementedException();
- }
- }
下列代碼片段顯示了您如何通過從ControllerBase類中推導來創建基本控制器類。
- [Route("api/[controller]")]
- [ApiController]
- public class BaseController : ControllerBase
- {
- protected readonly ILogger<BaseController> _logger;
- protected readonly IOrderManager _orderManager;
- public BaseController(ILogger<BaseController> logger,
- IOrderManager orderManager)
- {
- _logger = logger;
- _orderManager = orderManager;
- }
- }
ASP.NET Core中擴展自定義基本控制器
現在您可以創建控制器,只需從我們剛創建的這個自定義基本控制器來推導。下列代碼片段表明了您如何通過剛創建的基本控制器類加以擴展來創建控制器類。
- [Route("api/[controller]")]
- [ApiController]
- public class OrderController : BaseController
- {
- private readonly ILogger<OrderController> _logger;
- [HttpGet]
- public string Get()
- {
- return "OrderController";
- }
- [HttpPost]
- public void ProcessOrder(Order order)
- {
- _orderManager.ProcessOrder(order);
- }
- }
然而,您會發現上述代碼不會編譯。以下是您會在Visual Studio中看到的錯誤:
該錯誤表明,您需要使用與BaseController類構造函數相同的參數實現另一個參數構造函數。但為什么?如果您要在擴展所創建的基本控制器類的所有控制器中復制依賴項注入代碼,也就失去了擴展類的目的。
要解決此問題,您可以利用HttpContext.RequestServices.GetService
作為ASP.NET Core請求的一部分而存在的服務可通過HttpContext.RequestServices集合來訪問。這意味著當您請求服務時,將從該集合解析請求。
將HttpContext添加到ASP.NET Core中的基本控制器類
這是我們的BaseController 類的更新后源代碼。
- public abstract class BaseController<T> : ControllerBase where T : BaseController<T>
- {
- private ILogger<T> _logger;
- protected ILogger<T> Logger => _logger ?? (_logger = HttpContext.RequestServices.GetService<ILogger<T>>());
- }
OrderController類擴展這個抽象的BaseController類,如下面的代碼片段所示。
- [Route("api/[controller]")]
- [ApiController]
- public class OrderController : BaseController<OrderController>
- {
- private readonly IOrderManager _orderManager;
- public OrderController(IOrderManager orderManager)
- {
- _orderManager = orderManager;
- }
- [HttpGet]
- public string Get()
- {
- Logger.LogInformation("Hello World!");
- return "Inside the Get method of OrderController";
- }
- [HttpPost]
- public void ProcessOrder(Order order)
- {
- _orderManager.ProcessOrder(order);
- }
- }
就是這樣!OrderController可利用Logger實例,并利用構造函數注入來注入其他服務。
最后,記得將您的服務登記在Startup類的ConfigureServices方法中,所下所示:
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddTransient<IOrderManager,OrderManager>();
- services.AddControllersWithViews();
- }
最好使用參數化的構造函數來解析依賴項,比如通過使用構造函數注入。這將幫助您創建更易于測試和維護的類。
原文標題:How to avoid redundant DI code in ASP.NET Core,作者:Joydip Kanjilal
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】