我看到了在线帖子或视频,例如 https://www.youtube.com/watch?v=ykC3Ty-3U7g&t=588s,它总体上推广了 MediatR 或 Mediator 模式。
我不太明白使用 MediatR 或 Mediator 模式的好处
使用中介者模式:
Controler.Get() => await _sender.send(new GetAllProducts());
GetProductsHandler.Handle(...) => await _repo.GetAllProducts();
没有
Controler.GetProducts() => await _productServices.GetProducts();
ProductService.GetProducts() => await await _repo.GetAllProducts();
中介者似乎只是旧服务的更高级版本?
根据我的经验,使用 Mediator 有一些好处
首先你不需要在控制器中注入大量的服务和接口(只需要注入 IMediator 服务并使用它)
第二个也是更重要的一个是,您可以使用这个令人惊叹的库的一个非常有用的功能,即 PipelineBehavior check this out
这实际上取决于您的团队/个人偏好。显然,在您给出的示例中,与仅使用常规的旧构造函数注入相比,使用 MediatR 没有任何好处。 然而,我将强调以下几点,以我的拙见,MediatR 真正发挥了作用:
您需要进行大量构造函数注入(想想 4-5 个服务)。此时,命令/查询处理程序成为所有这些服务之间的编排,您可以保持控制器真正精简和最小化。 这实际上并没有多大作用,但在某些时候,如果您决定将项目移植到其他东西,比如从 ASP.NET 控制器移植到最小的 API 或 Azure Functions,甚至是 WinForm 应用程序之类的东西。您唯一需要做的就是将控制器重构为将请求发送到适当的处理程序的东西,然后一切就都准备好了。您的所有应用程序/业务逻辑保持不变。
MediatR 模式通常与分层架构一起使用,因此您的解决方案可能有多个项目负责不同的事情,老实说,任何中型项目最终都需要一些分层/切片。然后,通常在没有 MediatR 的情况下,您仍然需要通过某种服务将表示层与应用程序层或任何其他层分开。在这种情况下就说 GetProductService ,然后您需要将该服务注入到控制器中。这与使用 MediatR 没有太大区别,因为它可以扫描程序集并为您连接正确的处理程序,因此此时您可以自行完成或使用 MediatR 之类的东西。我看到的唯一缺点是,如果您以某种方式删除处理程序并且没有进行集成测试,您可能会遇到问题,或者在调试时,您不确定处理程序在哪里(可以通过健全的文件夹结构来缓解)。
您需要解决跨领域的问题(缓存/身份验证/授权/日志记录/验证等)。如果您深入研究 MediatR 库,您会发现一种称为管道行为的东西,它在实现时看起来像这样
public class LoggingBehavior<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly ILogger<TRequest> _logger;
public LoggingBehavior(ILogger<TRequest> logger)
=> _logger = logger;
// Log the request (command) and whether it succeeded or failed
public async Task<TResponse> Handle(
TRequest request,
CancellationToken ct,
RequestHandlerDelegate<TResponse> next)
{
// Get the name of the request
string name = request.GetType().Name;
try
{
// Log the request
_logger.LogInformation($"Executing request {name}", name);
// Execute the request
var result = await next();
_logger.LogInformation($"Request {name} executed successfully", name);
return result;
}
catch (Exception exception)
{
_logger.LogError(
exception,
$"Request {name} failed",
name);
throw;
}
}
}
此示例自动为您执行日志记录,您无需在任何处理程序中再次重复此代码。这有助于减少代码重复,并且可用于很多事情(授权/日志记录/验证等)。 如果没有 MediatR,您将需要构建项目来执行类似的操作以避免代码重复或使用重复的代码来在各处执行横切关注点。
public class AddNewProductDomainEventHandler: INotificationHandler<AddNewProductDomainEvent>
{
// your code to send email or whatever side effect that needed to do when a new product is added
}
在添加产品的处理程序中,现在您只需要执行此操作
// This will add new product to your db
await _repo.AddAsync(newProduct, ct)
// This will use IPublisher from MediatR to publish event
// As long as you have a handler for this event it will execute your side effect
await _publisher.Publish(new AddNewProductDomainEvent(product.Id))
注意,这只是一个示例,在现实生活中,您需要更好地构建它以实现弹性(发件箱模式或可以优雅地处理故障的模式)。这是事件驱动架构中非常常见的模式,当您需要在应用程序中执行类似的操作时,您将开始欣赏 MediatR。 当然,所有这些都可以由您自己/团队来实现,而无需添加其他外部依赖项。然而,行为会非常相似,因此您再次需要使用非常流行的东西,例如 MediatR 或 DIY 方法。