🌟 开篇:初识 MediatR

在.NET 的修仙世界中,MediatR 就像是一本神奇的"传音入密"秘籍,它能让你的代码各司其职,却又心意相通。今天,就让我们一起来修炼这本秘籍,掌握.NET 中的中介者模式精髓!

MediatR 是一个简单的中介者模式实现,它通过解耦消息发送者和接收者来简化应用程序中的进程内通信。简单来说,它就像是一个邮局,负责把你的"信件"(消息)准确无误地投递给正确的"收件人"(处理器)。

// 安装MediatR
dotnet add package MediatR

🏗️ 第一重境界:基础搭建

1. 配置 MediatR

首先,我们需要在 Startup.cs 或 Program.cs 中注册 MediatR 服务:

// .NET 6+ 的Program.cs
builder.Services.AddMediatR(cfg =>
    cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));

2. 创建第一个消息和处理器

让我们从一个简单的"Hello World"开始:

// 定义消息
public class HelloWorldQuery : IRequest<string>
{
    public string Name { get; set; }
}

// 定义处理器
public class HelloWorldHandler : IRequestHandler<HelloWorldQuery, string>
{
    public Task<string> Handle(HelloWorldQuery request, CancellationToken cancellationToken)
    {
        return Task.FromResult($"Hello, {request.Name}!");
    }
}

// 使用示例
var response = await mediator.Send(new HelloWorldQuery { Name = "修仙者" });
Console.WriteLine(response); // 输出: Hello, 修仙者!

🧩 第二重境界:命令与查询

MediatR 支持两种主要模式:命令(Command)和查询(Query),对应 CQRS 模式。

1. 命令模式示例

// 创建用户命令
public class CreateUserCommand : IRequest<int>
{
    public string Username { get; set; }
    public string Email { get; set; }
}

public class CreateUserHandler : IRequestHandler<CreateUserCommand, int>
{
    private readonly ApplicationDbContext _context;

    public CreateUserHandler(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
    {
        var user = new User
        {
            Username = request.Username,
            Email = request.Email
        };

        _context.Users.Add(user);
        await _context.SaveChangesAsync(cancellationToken);

        return user.Id;
    }
}

2. 查询模式示例

// 获取用户详情查询
public class GetUserByIdQuery : IRequest<UserDto>
{
    public int UserId { get; set; }
}

public class GetUserByIdHandler 
  : IRequestHandler<GetUserByIdQuery, UserDto>
{
    private readonly ApplicationDbContext _context;

    public GetUserByIdHandler(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<UserDto> Handle(
      GetUserByIdQuery request, 
      CancellationToken cancellationToken)
    {
        var user = await _context.Users
            .Where(u => u.Id == request.UserId)
            .Select(u => new UserDto
            {
                Id = u.Id,
                Username = u.Username,
                Email = u.Email
            })
            .FirstOrDefaultAsync(cancellationToken);

        return user ?? throw new NotFoundException("User not found");
    }
}

🔗 第三重境界:通知与事件

MediatR 还支持发布/订阅模式,通过 INotification 接口实现。

1. 定义通知事件

public class UserCreatedNotification : INotification
{
    public int UserId { get; set; }
    public string Username { get; set; }
    public DateTime CreatedAt { get; set; }
}

2. 创建多个处理器

// 发送欢迎邮件处理器
public class SendWelcomeEmailHandler : INotificationHandler<UserCreatedNotification>
{
    public async Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
    {
        // 模拟发送邮件
        Console.WriteLine($"发送欢迎邮件给 {notification.Username}");
        await Task.Delay(1000);
    }
}

// 记录用户创建日志处理器
public class LogUserCreatedHandler : INotificationHandler<UserCreatedNotification>
{
    private readonly ILogger<LogUserCreatedHandler> _logger;

    public LogUserCreatedHandler(ILogger<LogUserCreatedHandler> logger)
    {
        _logger = logger;
    }

    public Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
    {
        _logger.LogInformation("新用户创建: {UserId}, 用户名: {Username}",
            notification.UserId, notification.Username);
        return Task.CompletedTask;
    }
}

3. 发布通知

await mediator.Publish(new UserCreatedNotification
{
    UserId = newUserId,
    Username = command.Username,
    CreatedAt = DateTime.UtcNow
});

🛡️ 第四重境界:管道行为

MediatR 的管道行为(Pipeline Behaviors)类似于 ASP.NET Core 的中间件,可以在处理请求前后执行逻辑

1. 创建日志行为

public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;

    public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
    {
        _logger = logger;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        _logger.LogInformation("处理请求 {RequestName} {@Request}",
            typeof(TRequest).Name, request);

        try
        {
            var response = await next();
            _logger.LogInformation("请求 {RequestName} 处理成功", typeof(TRequest).Name);
            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "请求 {RequestName} 处理出错", typeof(TRequest).Name);
            throw;
        }
    }
}

2. 创建验证行为

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        if (_validators.Any())
        {
            var context = new ValidationContext<TRequest>(request);

            var validationResults = await Task.WhenAll(
                _validators.Select(v => v.ValidateAsync(context, cancellationToken)));

            var failures = validationResults
                .SelectMany(r => r.Errors)
                .Where(f => f != null)
                .ToList();

            if (failures.Count != 0)
                throw new ValidationException(failures);
        }

        return await next();
    }
}

3. 注册行为

builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

🎯 第五重境界:高级技巧

1. 使用 FluentValidation 进行验证

public class CreateUserCommandValidator : AbstractValidator<CreateUserCommand>
{
    public CreateUserCommandValidator()
    {
        RuleFor(x => x.Username)
            .NotEmpty().WithMessage("用户名不能为空")
            .MinimumLength(3).WithMessage("用户名至少3个字符")
            .MaximumLength(20).WithMessage("用户名最多20个字符");

        RuleFor(x => x.Email)
            .NotEmpty().WithMessage("邮箱不能为空")
            .EmailAddress().WithMessage("邮箱格式不正确");
    }
}

// 注册验证器
builder.Services.AddValidatorsFromAssemblyContaining<CreateUserCommandValidator>();

2. 使用特性标记处理器

// 自定义特性
[AttributeUsage(AttributeTargets.Class)]
public class TransactionalAttribute : Attribute
{
}

// 创建事务行为
public class TransactionalBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly ApplicationDbContext _context;

    public TransactionalBehavior(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        // 检查是否标记了Transactional特性
        if (request.GetType().GetCustomAttribute<TransactionalAttribute>() == null)
            return await next();

        await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken);

        try
        {
            var response = await next();
            await transaction.CommitAsync(cancellationToken);
            return response;
        }
        catch
        {
            await transaction.RollbackAsync(cancellationToken);
            throw;
        }
    }
}

3. 使用 MediatR 与 ASP.NET Core 集成

在控制器中使用 MediatR:

[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
    private readonly IMediator _mediator;

    public UsersController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost]
    public async Task<IActionResult> CreateUser([FromBody] CreateUserCommand command)
    {
        var userId = await _mediator.Send(command);
        return CreatedAtAction(nameof(GetUser), new { id = userId }, null);
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<UserDto>> GetUser(int id)
    {
        var query = new GetUserByIdQuery { UserId = id };
        var user = await _mediator.Send(query);
        return Ok(user);
    }
}

🌈 结语:MediatR 的修仙之道

1.通过今天的修炼,我们已经掌握了 MediatR 的五大境界:

  • ✅ 基础搭建 - 初识 MediatR
  • ✅ 命令与查询 - CQRS 模式实践
  • ✅ 通知与事件 - 发布/订阅模式
  • ✅ 管道行为 - 中间件式处理
  • ✅ 高级技巧 - 验证、事务等

2.MediatR 就像一把瑞士军刀,在.NET 应用程序中提供了灵活的消息传递机制。它能够:

  • ✅ 解耦组件之间的直接依赖
  • ✅ 简化控制器逻辑
  • ✅ 实现清晰的架构分层
  • ✅ 方便地添加横切关注点

记住,修仙之路漫长,MediatR 只是其中一本秘籍。希望这篇日记能助你在.NET 的修仙之路上更进一步!


💡 小测验:

  • 1.MediatR 主要实现了哪种设计模式?

  • 2.命令和查询在 MediatR 中有什么区别?

  • 3.如何为所有请求添加统一的日志记录?

欢迎在评论区分享你的答案和修炼心得!我们下期再见~

微信公众号【.NET修仙日记】

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐