明志唯新

MediatR 10.0 发布

发表于

MediatR

MediatR.NET 中的开源简单中介者模式实现.它通过一种进程内消息传递机制(无其他外部依赖),进行请求/响应、命令、查询、通知和事件的消息传递,并通过泛型来支持消息的智能调度。开源库地址是 https://github.com/jbogard/MediatR

详细介绍,可见我之前的文章《MediatR 在 .NET 应用中的实践

MediatR 10.0

2022年1月7日,MediatR 的作者发布了 10.0 。该版本主要有如下几个更新:

支持 Stream

即通过新的 IStreamRequest 来获得 IAsyncEnumerable 的响应结果; 可以通过类似下面的代码形式,定义 StreamRequest 和对应的 Handler

public class Sing : IStreamRequest<Song>
{
    public string Message { get; set; }
}

public class Song
{
    public string Message { get; set; }
}

public class SingHandler : IStreamRequestHandler<Sing, Song>
{
    private readonly TextWriter _writer;

    public SingHandler(TextWriter writer)
    {
        _writer = writer;
    }

    public async IAsyncEnumerable<Song> Handle(Sing request, [EnumeratorCancellation]CancellationToken cancellationToken)
    {
        await _writer.WriteLineAsync($"--- Handled Sing: {request.Message}, Song");
        yield return await Task.Run(() => new Song { Message = request.Message + "ing do" });
        yield return await Task.Run(() => new Song { Message = request.Message + "ing re" });
        yield return await Task.Run(() => new Song { Message = request.Message + "ing mi" });
        yield return await Task.Run(() => new Song { Message = request.Message + "ing fa" });
        yield return await Task.Run(() => new Song { Message = request.Message + "ing so" });
        yield return await Task.Run(() => new Song { Message = request.Message + "ing la" });
        yield return await Task.Run(() => new Song { Message = request.Message + "ing ti" });
        yield return await Task.Run(() => new Song { Message = request.Message + "ing do" });
    }
}

然后我们就用通过异步 foreach 来进行调用了:

await foreach (Song s in mediator.CreateStream(new Sing { Message = "Sing" }))
{
  // 根据流式响应的结果进行业务处理
}

也就是说这种 StreamRequest 我们需要通过 IMediatorCreateStream 方法来调用处理程序。

可以想到在一些需要异步跟踪长任务进度或者聊天交出程序等场景下蛮有用的。

新增一个独立的包 MediatR.Contracts

这样你就可以将 Request / Notification / StreamRequest 通过仅引用该包在一个独立的项目中定义,而将相关的 Handler在另外一个项目中实现。例如下列场景:

  • API contracts
  • GRPC contracts
  • Blazor

Break Changes

当我们从 9.x 版本 升级到 10.0 时,需要注意一下管线相关的定义要稍微修改一下,因为 IPipelineBehavior 接口中的泛型约束做了一点点修改。

  • IPipelineBehavior 的泛型约束 从 where TRequest notnull 修改为 where TRequest : IRequest<TResponse>
  • IRequestExceptionHandler 的泛型约束 从 where TRequest notnull 修改为 where TRequest : IRequest<TResponse>
  • IRequestPostProcessor 的泛型约束 从 where TRequest notnull 修改为 where TRequest : IRequest<TResponse>
  • IRequestPostProcessor 的泛型约束 从 where TRequest notnull 修改为 where TRequest : IRequest<TResponse>

所以,我们在使用 MediatR 的代码中如果有自定义的管线代码,需要在原来的基础上增加一点泛型约束即可,否则编译无法通过:

public class GenericPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
}