使用 MediatR 与直接使用 Service 相比的好处

问题描述 投票:0回答:2

我看到了在线帖子或视频,例如 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();

中介者似乎只是旧服务的更高级版本?

c# mediator
2个回答
1
投票

根据我的经验,使用 Mediator 有一些好处

首先你不需要在控制器中注入大量的服务和接口(只需要注入 IMediator 服务并使用它)

第二个也是更重要的一个是,您可以使用这个令人惊叹的库的一个非常有用的功能,即 PipelineBehavior check this out


0
投票

这实际上取决于您的团队/个人偏好。显然,在您给出的示例中,与仅使用常规的旧构造函数注入相比,使用 MediatR 没有任何好处。 然而,我将强调以下几点,以我的拙见,MediatR 真正发挥了作用:

  1. 您需要进行大量构造函数注入(想想 4-5 个服务)。此时,命令/查询处理程序成为所有这些服务之间的编排,您可以保持控制器真正精简和最小化。 这实际上并没有多大作用,但在某些时候,如果您决定将项目移植到其他东西,比如从 ASP.NET 控制器移植到最小的 API 或 Azure Functions,甚至是 WinForm 应用程序之类的东西。您唯一需要做的就是将控制器重构为将请求发送到适当的处理程序的东西,然后一切就都准备好了。您的所有应用程序/业务逻辑保持不变。

  2. MediatR 模式通常与分层架构一起使用,因此您的解决方案可能有多个项目负责不同的事情,老实说,任何中型项目最终都需要一些分层/切片。然后,通常在没有 MediatR 的情况下,您仍然需要通过某种服务将表示层与应用程序层或任何其他层分开。在这种情况下就说 GetProductService ,然后您需要将该服务注入到控制器中。这与使用 MediatR 没有太大区别,因为它可以扫描程序集并为您连接正确的处理程序,因此此时您可以自行完成或使用 MediatR 之类的东西。我看到的唯一缺点是,如果您以某种方式删除处理程序并且没有进行集成测试,您可能会遇到问题,或者在调试时,您不确定处理程序在哪里(可以通过健全的文件夹结构来缓解)。

  3. 您需要解决跨领域的问题(缓存/身份验证/授权/日志记录/验证等)。如果您深入研究 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,您将需要构建项目来执行类似的操作以避免代码重复或使用重复的代码来在各处执行横切关注点。

  1. 最后一点是关于内存中事件发布的。 MediatR 还支持通过 INotificationHandler 发布和处理内存中事件,不要将其与微服务中的外部事件混淆。这个想法是,您可以通过发布事件而不是直接执行来解决系统中的副作用。让我们以您的产品为例,假设您有一个用例将新产品添加到库存中,那么您还需要发送一封电子邮件来通知对此感兴趣的任何人。虽然您可以在单个服务中执行所有这些操作,但最好将操作分开并将新产品添加到数据库,然后发布一个事件以单独发送电子邮件
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 方法。

© www.soinside.com 2019 - 2024. All rights reserved.