【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】