在本文中, 我将解释命令模式, 以及如何利用基于命令模式的第三方库来实现它们, 以及如何在 ASP.NET Core 中使用它来解决我们的问题并使代码简洁. 因此, 我们将通过下面的主题来进行相关的讲解.
什么是命令模式?
命令模式的简单实例以及中介者模式的简单描述
MVC 中的瘦控制器是什么? 我们是如如何实现使控制器变瘦的?
我们如何在我们的. NET Core 应用程序中使用 MediatR
使用命令和事件的实例
命令模式及其简单实例
从根本上讲, 命令模式是一种数据驱动的设计模式, 属于行为模式的范畴. 命令是我们可以执行的某种操作或行为, 它可以是活动的一部分. 一个活动可以有一个或多个命令和实现.
我们可以这样来说, 请求以命令的形式包裹在对象中, 并传给调用对象. 调用者 (代理) 对象查找可以处理该命令的合适的对象, 并把该命令传给相应的对象, 该对象执行命令 .
一个简单的例子是多种类型的消息. Message 类包含 SendEmail()和 SendSms()等属性和方法. 使用两种类型的命令, 并且需要一个接口, 它应该由实现了 EmailMessageCommand 和 SMSMessageCommand 的类类继承. 还使用代理类来调用特定类型的消息类来处理操作.
- Main class
- class Program
- {
- static void Main(string[] args)
- {
- Message message = new Message();
- message.CustomMessage = "Welcome by Email";
- EmailMessageCommand emailMessageCommand = new EmailMessageCommand(message);
- Message message2 = new Message();
- message2.CustomMessage = "Welcome by SMS";
- SmsMessageCommand smsMessageCommand = new SmsMessageCommand(message2);
- Broker broker = new Broker();
- broker.SendMessage(emailMessageCommand);
- broker.SendMessage(smsMessageCommand);
- Console.ReadKey();
- }
- }
消息类
- public class Message
- {
- public string CustomMessage { get; set; }
- public void EmailMessage()
- {
- Console.WriteLine($"{CustomMessage} : Email Message sent");
- }
- public void SmsMessage()
- {
- Console.WriteLine($"{CustomMessage} : Sms Message sent");
- }
- }
接口和代理类
- public interface IMessageCommand
- {
- void DoAction();
- }
- public class Broker
- {
- public void SendMessage(IMessageCommand command)
- {
- command.DoAction();
- }
- }
命令
- public class EmailMessageCommand : IMessageCommand
- {
- private Message oMessage;
- public EmailMessageCommand(Message oMessage)
- {
- this.oMessage = oMessage;
- }
- public void DoAction()
- {
- oMessage.EmailMessage();
- }
- }
- public class SmsMessageCommand : IMessageCommand
- {
- private Message oMessage;
- public SmsMessageCommand(Message oMessage)
- {
- this.oMessage = oMessage;
- }
- public void DoAction()
- {
- oMessage.SmsMessage();
- }
- }
输出
什么是瘦控制器, 我们为什么需要它? 什么是 MediatR?
当我们开始使用 MVC 框架进行开发时, 逻辑是用控制器的动作方法编写的; 就像我们有一个简单的电子商务应用程序, 其中用户应该会下订单. 我们有一个控制器, OrderController, 用来管理订单. 当用户下订单时, 我们应该在数据库中保存记录.
在此之前, 我们有一个简化的代码. 然而, 经过一段时间后, 我们意识到还有一个确认电子邮件的业务需求. 现在, 第二步是发送确认电子邮件给客户. 后来, 我们意识到, 在这个步骤之后, 我们还需要执行另一个操作, 即, 记录信息等. 最后, 我们还需要将用户的信息保存到 CRM 中. 关键是它会增长控制器的大小. 现在, 我们可以称之为 "臃肿控制器".
基于命令的体系结构允许我们发送命令来执行某些操作, 并且我们有单独的命令处理程序, 使关注点分离和提高单一职责. 为了实现这个架构, 我们可以使用第三方库, 比如 MediatR(Mediator.), 它为我们做了很多基础工作. 中介模式定义了一个对象, 该对象封装了一组对象是如何交互的.
中介模式的优势及 MediatR 如何帮助我们实现中介模式
中介模式定义了一个对象, 该对象封装了一组对象是如何交互的(如维基百科定义的).
它通过保持对象彼此明确地相互引用来促进松散耦合.
它通过允许通信被卸载到一个只处理这类的类来促进单一责任原则.
MediatR 库如何帮助我们
MediatR 允许我们通过让控制器 Action 向处理程序发送请求消息来将控制器与业务逻辑解耦. MediatR 库支持两种类型的操作.
(预期输出结果)
事件(请求者不关心接下来发生了什么, 不期待结果)
我们已经介绍了命令模式, 因此是时候定义一些命令并使用 MediatR 发出命令了.
在 ASP.NET Core 中安装
我们需要从 NuGet 安装 MediatR 和 MediatR.Extensions.Microsoft.DependencyInjection 包.
当这两个软件包安装完毕后, 我们需要添加 services.AddMediatR(); 到 startup.cs 文件. 看起来像这样.
现在, 我们可以使用. NET Core 项目中的 MediatR 了.
实例
第一个示例演示了使用 MediatR 使用请求 / 响应类型的操作. 它期望对请求做出一些反应.
第二个示例将向您展示一个事件, 其中多个处理程序执行它们的工作, 调用者并不关心接下来会发生什么, 也不期望任何结果 / 响应.
第一个例子
在这种场景下, 我们希望注册用户并期望对请求做出一些响应. 如果响应返回 true, 我们可以像登录用户一样进行进一步的操作.
首先, 我们需要创建一个继承自 IRequest 的类.
- public class NewUser: IRequest<bool>
- {
- public string Username { get; set; }
- public string Password { get; set; }
- }
IRequest 是指请求的响应是布尔响应.
现在, 需要一个处理程序来处理这种类型的请求.
- public class NewUserHandler : IRequestHandler<NewUser, bool>
- {
- public Task<bool> Handle(NewUser request, CancellationToken cancellationToken)
- {
- // save to database
- return Task.FromResult(true);
- }
- }
现在我们有了命令和它的处理程序, 我们可以调用 MediatR 在我们的控制器中做一些操作.
这些是 Home 控制器的动作方法.
- public class HomeController : Controller
- {
- private readonly IMediator _mediator;
- public HomeController(IMediator mediator)
- {
- _mediator = mediator;
- }
- [HttpGet]
- public ActionResult Register()
- {
- return View();
- }
- [HttpPost]
- public ActionResult Register(NewUser user)
- {
- bool result = _mediator.Send(user).Result;
- if (result)
- return RedirectToAction("Login");
- return View();
- }
- }
第一个例子的结论
注册操作方法使用了 [HttpPost] 属性进行修饰, 并接受新的用户注册请求. 然后, 它请求 MediatR 进行处理. 它期望来自请求的结果 / 响应, 如果结果是真的, 则将用户重定向到登录页面.
这里, 我们有简洁的代码, 大部分的工作是在控制器外部完成的. 这实现了对不同操作的处理的关注点分离 (SoC) 和单一责任的分离.
在第二个示例中, 我们将演示使用多个处理程序对命令执行不同操作的场景.
第二个实例
在这种情况下, 我们使 NewUser 继承了 INotification
- public class NewUser : INotification
- {
- public string Username { get; set; }
- public string Password { get; set; }
- }
现在, 有三个处理程序逐个执行, 以完成他们的工作. 这些都是从 INotificationHandler 继承下来的.
- public class NewUserHandler : INotificationHandler<NewUser>
- {
- public Task Handle(NewUser notification, CancellationToken cancellationToken)
- {
- //Save to log
- Debug.WriteLine("**** Save user in database *****");
- return Task.FromResult(true);
- }
- }
第二个处理程序在下面的代码中定义.
- public class EmailHandler : INotificationHandler<NewUser>
- {
- public Task Handle(NewUser notification, CancellationToken cancellationToken)
- {
- //Send email
- Debug.WriteLine("**** Email sent to user *****");
- return Task.FromResult(true);
- }
- }
这是第三个处理程序的代码
- public class LogHandler : INotificationHandler<NewUser>
- {
- public Task Handle(NewUser notification, CancellationToken cancellationToken)
- {
- //Save to log
- Debug.WriteLine("**** User save to log *****");
- return Task.FromResult(true);
- }
- }
然后, 我们控制器中的代码像下面这样
- public class AccountsController : Controller
- {
- private readonly IMediator _mediator;
- public AccountsController(IMediator mediator)
- {
- _mediator = mediator;
- }
- [HttpGet]
- public ActionResult Login()
- {
- return View();
- }
- [HttpGet]
- public ActionResult Register()
- {
- return View();
- }
- [HttpPost]
- public ActionResult Register(NewUser user)
- {
- _mediator.Publish(user);
- return RedirectToAction("Login");
- }
- }
第二个例子的结论
此应用程序的输出如下:
当用户注册后, 三个处理程序逐个执行 -- 分别是 NewUserHandler,EmailHandler 和 LogHandler, 并执行它们的操作.
这里, 我们使用了 Publish 方法, 而不是 Send 函数. 发布将调用订阅了 NewUser 类的所有处理程序. 这只是一个示例, 我们可以根据命令进行思考, 然后按照我们在命令模式中讨论的方式相应地执行一些操作.
Mediatr 是如何提供帮助的?
它可以用来隐藏实现的细节, 用来使控制器代码更加干净和可维护, 可以重用多个处理程序, 并且每个处理程序都有自己的责任, 因此易于管理和维护.
在我的下一篇文章中, 我将尝试解释 CQRS 架构模式及其优点以及如何使用 MediatR 来实现 CQRS.
来源: https://www.cnblogs.com/yilezhu/p/9866068.html