有没有办法跳过 MediatR 管道?
Is there a way to skip MediatR Pipeline?
我想缓存一些来自 CommandsHandlers 的响应。
我已经使用 IPipelineBehaviour 完成了此操作,但只有 5% 的请求确实必须具有缓存,而其他 95% 的请求必须跳过此管道。有办法吗?
下面是我的代码。
谢谢!
public class PipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, IProvideCacheKey
{
private readonly IMemoryCache _cache;
public PipelineBehavior(IMemoryCache cache)
{
_cache = cache;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
// Check in cache if we already have what we're looking for
var cacheKey = request.CacheKey;
if (_cache.TryGetValue<TResponse>(cacheKey, out var cachedResponse))
{
return cachedResponse;
}
// If we don't, execute the rest of the pipeline, and add the result to the cache
var response = await next();
_cache.Set(cacheKey, response);
return response;
}
}
public class GetUserByEmailCommand : Command, IRequest<bool>, IProvideCacheKey
{
public string Email { get; set; }
public string CacheKey => $"{GetType().Name}:{Email}";
public override bool IsValid()
{
ValidationResult = new GetUserByEmailCommandValidation<GetUserByEmailCommand>().Validate(this);
return ValidationResult.IsValid;
}
}
public interface IProvideCacheKey
{
string CacheKey { get; }
}
您可以将缓存行为包装在一个检查中,如果请求不可缓存以让管道继续,则绕过该检查。在您的情况下,您可能只检查请求是否在 Handle 方法的开头实现了您的接口:
if (request is IProvideCacheKey)
{
// perform cache behavior, return if cached and terminate the pipeline
}
// else continue the pipeline
有几个很好的例子可以更详细地说明这一点:
https://lurumad.github.io/cross-cutting-concerns-in-asp-net-core-with-meaditr
https://anderly.com/2019/12/12/cross-cutting-concerns-with-mediatr-pipeline-behaviors/
我遇到了同样的问题。
如果请求实现了接口,我通过检查 管道行为处理程序 来修复它。
如果请求实现接口 ICachable
,这意味着请求将通过可缓存逻辑。
进行此更改后,无需使用 where TRequest : IQueryable
.
If you leave this constrain it will throw an error when trying to
process a Query or Command that doesn't implement the IQueryable
interface.
如果您想知道如何访问接口属性,答案是创建一个实现该接口的变量。
theObject is theInterface newObjectWithInterfaceProperties
.
{
public class CacheBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
//where TRequest : ICacheableQuery
{
private readonly IMemoryCache _memoryCache;
private readonly ILogger<CacheBehaviour<TRequest, TResponse>> _logger;
public CacheBehaviour(IMemoryCache memoryCache, ILogger<CacheBehaviour<TRequest, TResponse>> logger)
{
_memoryCache = memoryCache;
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
//Check if the request implements ICachableQuery
//If it does then it creates a cachableRequest variable that will contain the properties of the ICachableQuery interface.
if (request is ICachableQuery cachableRequest)
{
var requestName = request.GetType().Name;
_logger.LogInformation($"Request : {requestName} is configured to cache");
TResponse response;
if(_memoryCache.TryGetValue(cachableRequest.CacheKey, out response))
{
_logger.LogInformation($"Request: {requestName} returning response from cache");
return response;
}
response = await next();
_logger.LogInformation($"Request: {requestName} returning response from DB");
_memoryCache.Set(cachableRequest.CacheKey, response);
return response;
}
return await next();
}
}
}
与其跳过行为,不如构建多个具有不同行为的管道。
注册管道行为:
// pipeline 1's behaviors
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(FooBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(BarBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(BazBehavior<,>));
// pipeline 2's behaviors
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(CatBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(DogBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(PigBehavior<,>));
定义管道:
// pipeline 1
public interface IPipeline1 { }
public class FooBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline1 { }
public class BarBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline1 { }
public class BazBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline1 { }
// pipeline 2
public interface IPipeline2 { }
public class CatBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline2 { }
public class DogBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline2 { }
public class PigBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline2 { }
定义请求:
// requests to be processed by pipeline 1
public class ARequest : IRequest, IPipeline1 { }
public class BRequest : IRequest, IPipeline1 { }
public class CRequest : IRequest, IPipeline1 { }
// requests to be processed by pipeline 2
public class XRequest : IRequest, IPipeline2 { }
public class YRequest : IRequest, IPipeline2 { }
public class ZRequest : IRequest, IPipeline2 { }
我想缓存一些来自 CommandsHandlers 的响应。
我已经使用 IPipelineBehaviour 完成了此操作,但只有 5% 的请求确实必须具有缓存,而其他 95% 的请求必须跳过此管道。有办法吗?
下面是我的代码。
谢谢!
public class PipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, IProvideCacheKey
{
private readonly IMemoryCache _cache;
public PipelineBehavior(IMemoryCache cache)
{
_cache = cache;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
// Check in cache if we already have what we're looking for
var cacheKey = request.CacheKey;
if (_cache.TryGetValue<TResponse>(cacheKey, out var cachedResponse))
{
return cachedResponse;
}
// If we don't, execute the rest of the pipeline, and add the result to the cache
var response = await next();
_cache.Set(cacheKey, response);
return response;
}
}
public class GetUserByEmailCommand : Command, IRequest<bool>, IProvideCacheKey
{
public string Email { get; set; }
public string CacheKey => $"{GetType().Name}:{Email}";
public override bool IsValid()
{
ValidationResult = new GetUserByEmailCommandValidation<GetUserByEmailCommand>().Validate(this);
return ValidationResult.IsValid;
}
}
public interface IProvideCacheKey
{
string CacheKey { get; }
}
您可以将缓存行为包装在一个检查中,如果请求不可缓存以让管道继续,则绕过该检查。在您的情况下,您可能只检查请求是否在 Handle 方法的开头实现了您的接口:
if (request is IProvideCacheKey)
{
// perform cache behavior, return if cached and terminate the pipeline
}
// else continue the pipeline
有几个很好的例子可以更详细地说明这一点:
https://lurumad.github.io/cross-cutting-concerns-in-asp-net-core-with-meaditr
https://anderly.com/2019/12/12/cross-cutting-concerns-with-mediatr-pipeline-behaviors/
我遇到了同样的问题。
如果请求实现了接口,我通过检查 管道行为处理程序 来修复它。
如果请求实现接口 ICachable
,这意味着请求将通过可缓存逻辑。
进行此更改后,无需使用 where TRequest : IQueryable
.
If you leave this constrain it will throw an error when trying to process a Query or Command that doesn't implement the IQueryable interface.
如果您想知道如何访问接口属性,答案是创建一个实现该接口的变量。
theObject is theInterface newObjectWithInterfaceProperties
.
{
public class CacheBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
//where TRequest : ICacheableQuery
{
private readonly IMemoryCache _memoryCache;
private readonly ILogger<CacheBehaviour<TRequest, TResponse>> _logger;
public CacheBehaviour(IMemoryCache memoryCache, ILogger<CacheBehaviour<TRequest, TResponse>> logger)
{
_memoryCache = memoryCache;
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
//Check if the request implements ICachableQuery
//If it does then it creates a cachableRequest variable that will contain the properties of the ICachableQuery interface.
if (request is ICachableQuery cachableRequest)
{
var requestName = request.GetType().Name;
_logger.LogInformation($"Request : {requestName} is configured to cache");
TResponse response;
if(_memoryCache.TryGetValue(cachableRequest.CacheKey, out response))
{
_logger.LogInformation($"Request: {requestName} returning response from cache");
return response;
}
response = await next();
_logger.LogInformation($"Request: {requestName} returning response from DB");
_memoryCache.Set(cachableRequest.CacheKey, response);
return response;
}
return await next();
}
}
}
与其跳过行为,不如构建多个具有不同行为的管道。
注册管道行为:
// pipeline 1's behaviors
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(FooBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(BarBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(BazBehavior<,>));
// pipeline 2's behaviors
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(CatBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(DogBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(PigBehavior<,>));
定义管道:
// pipeline 1
public interface IPipeline1 { }
public class FooBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline1 { }
public class BarBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline1 { }
public class BazBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline1 { }
// pipeline 2
public interface IPipeline2 { }
public class CatBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline2 { }
public class DogBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline2 { }
public class PigBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IPipeline2 { }
定义请求:
// requests to be processed by pipeline 1
public class ARequest : IRequest, IPipeline1 { }
public class BRequest : IRequest, IPipeline1 { }
public class CRequest : IRequest, IPipeline1 { }
// requests to be processed by pipeline 2
public class XRequest : IRequest, IPipeline2 { }
public class YRequest : IRequest, IPipeline2 { }
public class ZRequest : IRequest, IPipeline2 { }