- Published on
ASP.NET Core 實作 MediatR 的 Pipeline 功能
之前的兩篇文章已經把 MediatR 的基本功能都介紹過了,現在要介紹 Pipeline 的功能
範例 Repository 在 Github
新增 LogPreProcessor 實作 IRequestPreProcessor
- IRequestPreProcessor指的是每一個 Request- 之前都會執行一次,泛型的型別就是實作- IRequest的型別,因為是在 Request 之前,所以在- Process裡面只拿的到- Request的資料
public class LogPreProcessor<TRequest> : IRequestPreProcessor<TRequest>
{
    private readonly ILogger<TRequest> _logger;
    public LogPreProcessor(ILogger<TRequest> logger)
    {
        _logger = logger;
    }
    public Task Process(TRequest request, CancellationToken cancellationToken)
    {
        _logger.LogInformation($"{nameof(LogPreProcessor<TRequest>)} Request -- {JsonConvert.SerializeObject(request)}");
        return Task.CompletedTask;
    }
}
- 在 Startup裡面去註冊剛才新增的LogPreProcessor
public void ConfigureServices(IServiceCollection services)
{
    services.AddMediatR(typeof(AddPersonCommand).GetTypeInfo().Assembly);
    services.AddTransient(typeof(IRequestPreProcessor<>), typeof(LogPreProcessor<>));
}
- 在這裡使用之前的新增 Person 來作測試,可以看到有把 Request 給 Log 下來

新增 LogPostProcessor 實作 IRequestPostProcessor
- IRequestPostProcessor指的是每一個 Request- 之後都會執行一次,泛型的型別有兩個,就是實作- IRequest的型別和- Response的型別,因為是在 Request 之後,所以在- Process可以拿的的到- Request和- Response的資料
public class LogPostProcessor<TRequest, TResponse> : IRequestPostProcessor<TRequest, TResponse>
{
    private readonly ILogger<TRequest> _logger;
    public LogPostProcessor(ILogger<TRequest> logger)
    {
        _logger = logger;
    }
    public Task Process(TRequest request, TResponse response)
    {
        _logger.LogInformation($"{nameof(LogPostProcessor<TRequest, TRequest>)} Request -- {JsonConvert.SerializeObject(request)}");
        _logger.LogInformation($"{nameof(LogPostProcessor<TRequest, TResponse>)} Response -- {JsonConvert.SerializeObject(response)}");
        return Task.CompletedTask;
    }
}
- 在 Startup裡面把原本註冊的LogPreProcessor改成LogPostProcessor
public void ConfigureServices(IServiceCollection services)
{
    services.AddMediatR(typeof(AddPersonCommand).GetTypeInfo().Assembly);
    services.AddTransient(typeof(IRequestPostProcessor<>), typeof(LogPostProcessor<>));
}
- 測試

新增 LogBehavior 實作 IPipelineBehavior
- IPipelineBehavior和 ASP.NET Core 的- Middleware很像,裡面有- next可以使用,可以自行決定在呼叫- IRequestHandler的前或後要作什麼,泛型的型別有兩個,實作- IRequest和- Response的型別
   public class LogBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    {
        private readonly ILogger<TRequest> _logger;
        public LogBehavior(ILogger<TRequest> logger)
        {
            _logger = logger;
        }
        public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
        {
            _logger.LogInformation($" LogBehavior Request -- {JsonConvert.SerializeObject(request)}");
            var response = await next();
            _logger.LogInformation($" LogBehavior Response -- {JsonConvert.SerializeObject(response)}");
            return response;
        }
    }
- 在 Startup裡面把註冊的型別換成LogBehavior
public void ConfigureServices(IServiceCollection services)
{
    services.AddMediatR(typeof(AddPersonCommand).GetTypeInfo().Assembly);
    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LogBehavior<,>));
}
- 測試

組合不同的 IPipelineBehavior
- 在新增一個 RequestPerformanceBehavior,用來測量 Request 的執行時間
public class RequestPerformanceBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly Stopwatch _timer;
    private readonly ILogger<TRequest> _logger;
    public RequestPerformanceBehavior(ILogger<TRequest> logger)
    {
        _timer = new Stopwatch();
        _logger = logger;
    }
    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        _timer.Start();
        var response = await next();
        _timer.Stop();
        _logger.LogInformation($"{nameof(RequestPerformanceBehavior<TRequest, TRequest>)} running time - {_timer.ElapsedMilliseconds}");
        return response;
    }
}
- 在 Startup裡面註冊RequestPerformanceBehavior,加到最後面
public void ConfigureServices(IServiceCollection services)
{
    services.AddMediatR(typeof(AddPersonCommand).GetTypeInfo().Assembly);
    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LogBehavior<,>));
	services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPerformanceBehavior<,>));
}
- 測試,可以拿到執行 Request 的時間出現在 Log 前面

- 如果調整順序把 RequestPerformanceBehavior移到 Pipeline 的第一個
public void ConfigureServices(IServiceCollection services)
{
    services.AddMediatR(typeof(AddPersonCommand).GetTypeInfo().Assembly);
	services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPerformanceBehavior<,>));
	services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LogBehavior<,>));
}
- 再一次測試,可以拿到執行 Request 的時間出現在 Log 後面,也就是放的順序和出現的順序是相反的

後記
MediatR 有提供 Pipeline 的功能,某種程度上面還蠻方便的,不過也因為它的功能沒有那麼強大,所以使用的範圍來說還是有限的,如果需要作到比較多的事情,使用 ActionFilter 和 Middleware 還是比較方便一些
