有没有办法跳过 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 { }