MediatR (两种模式) 使用示例以及区别

MediatR 是一个开源的.NET库,用于实现中介者模式,帮助开发者在应用程序中实现低耦合的通信。它主要用于实现命令查询责任分离(CQRS)模式,这意味着将应用程序中的读操作(查询)与写操作(命令)明确分离。通过MediatR,开发者可以将请求的发送者和请求的处理者解耦,提高代码的可维护性和可测试性。

在使用MediatR时,开发者定义请求(命令或查询)和对应的处理器。当请求被发送时,MediatR负责将其路由到正确的处理器上。这个过程是通过依赖注入(DI)容器自动完成的,无需手动编写大量的路由逻辑。

MediatR支持同步和异步请求处理,也支持发布/订阅模式,使得开发者可以轻松地在应用程序中实现事件驱动的架构。

简单来说,MediatR通过提供一个中央位置来处理所有的请求和消息,帮助开发者构建出更清晰、更易于管理的应用程序架构。

1. CQRS(发送命令模式)模式使用示例

//创建命令
public class CreateUserCommand : IRequest<bool> // 返回类型为bool,表示操作成功与否
{
    public string UserName { get; set; }
    public string Email { get; set; }

    public CreateUserCommand(string userName, string email)
    {
        UserName = userName;
        Email = email;
    }
}
//实现具体处理
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, bool>
{
    private readonly ILogger<CreateUserCommandHandler> _logger;
    public CreateUserCommandHandler(ILogger<CreateUserCommandHandler> logger)
    {
        _logger = logger;
    }
    public async Task<bool> Handle(CreateUserCommand request, CancellationToken cancellationToken)
    {
        // 在这里实现创建用户的逻辑
        // 假设我们直接返回true表示用户创建成功
        _logger.LogInformation($"用户: {request.UserName} 创建成功");
        return await Task.FromResult(true);
    }
}
//依赖注入注册MediatR
services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssemblies(AppDomain.CurrentDomain.GetAssemblies());
});
//使用示例
public class SendUserCommandWorker : BackgroundService
{
    private readonly IMediator _mediator;
    public SendUserCommandWorker(IMediator mediator)
    {
        _mediator = mediator;
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var command = new CreateUserCommand("张三", "test@example.com");
        await _mediator.Send(command);
    }
}

2. 发布/订阅模式使用示例

//创建事件
public class UserCreatedEvent : INotification
{
    public string UserName { get; }

    public UserCreatedEvent(string userName)
    {
        UserName = userName;
    }
}
//实现具体处理
public class EmailNotificationHandler : INotificationHandler<UserCreatedEvent>
{
    public Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Sending email to {notification.UserName}.");
        // 在这里实现发送邮件的逻辑
        return Task.CompletedTask;
    }
}
public class LogNotificationHandler : INotificationHandler<UserCreatedEvent>
{
    public Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Logging user creation: {notification.UserName}.");
        // 在这里实现日志记录的逻辑
        return Task.CompletedTask;
    }
}
//依赖注入注册MediatR
services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssemblies(AppDomain.CurrentDomain.GetAssemblies());
});
//使用示例
public class SendUserCommandWorker : BackgroundService
{
    private readonly IMediator _mediator;
    public SendUserCommandWorker(IMediator mediator)
    {
        _mediator = mediator;
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await _mediator.Publish(new UserCreatedEvent("张三"));
    }
}

注意: 两种模式的区分

发布/订阅模式和发送命令模式是两种不同的通信机制,它们在MediatR库中被用来解决不同的问题。理解它们之间的区别有助于明确何时使用哪一种模式。

发布/订阅模式

  • 定义:在发布/订阅模式中,当某个事件发生时,发布者会发布一个事件,所有订阅了这个事件的处理器都会被通知并执行相应的操作。这是一种一对多的通信方式。
  • 用例:这种模式通常用于那些需要多个系统或组件响应同一事件的场景。例如,在用户创建成功后,你可能需要发送欢迎邮件、更新统计数据、通知其他系统等,每一项操作都可以通过订阅用户创建事件来实现。
  • 示例await _mediator.Publish(new UserCreatedEvent(count.ToString())); 在这个例子中,UserCreatedEvent事件被发布,所有订阅了这个事件的处理器都会执行。

发送命令模式

  • 定义:发送命令模式是一种一对一的通信方式,请求发送者发送一个命令,寻找一个对应的处理器来处理这个命令。
  • 用例:这种模式适用于执行具体的操作或更改,通常这些操作或更改是单一的、明确的。比如,创建一个用户、更新一个订单等。
  • 示例await _mediator.Send(command); 在这个例子中,CreateUserCommand命令被发送,MediatR找到对应的处理器来执行这个命令。

为何要区分?

  • 解耦合:通过区分这两种模式,MediatR帮助进一步降低系统内部的耦合度。不同的场景需要不同的解耦策略,明确区分这两种模式可以让设计更加清晰。
  • 明确意图:发布/订阅模式和发送命令模式从语义上表达了开发者的不同意图:前者是通知一个事件发生了,后者是请求执行一个具体操作。
  • 灵活性:在实际应用中,同一个操作可能既需要执行具体的命令,也需要通知其他组件。区分这两种模式使得开发者可以根据场景灵活选择,或者同时使用这两种模式。

总之,通过区分发布/订阅模式和发送命令模式,MediatR提供了一种灵活而强大的方式来构建应用程序的内部通信,使得代码更加模块化、易于维护和扩展。