我有很多命令和查询,其中大多数都需要相同的接口DI来做不同的事情。是否可以通过某种方式减少我的处理程序中每一个需要的杂乱无章并不断重复的麻烦?

public class GetCoinByIdQueryHandler : IRequestHandler<GetCoinByIdQuery, CoinModel>
{
    private readonly EventsContext context;
    private readonly ICacheClient cache;
    private readonly ILogger logger;
    private readonly IMapper mapper;
    private readonly Settings settings;

    public GetCoinByIdQueryHandler(
        EventsContext context, ICacheClient cache, ILogger logger,
        IMapper mapper, IOptions<Settings> settings)
    {
        this.context = context;
        this.cache = cache;
        this.logger = logger;
        this.mapper = mapper;
        this.settings = settings.Value;
    }
 }


这可能与Mediatr没有直接关系,但我正在寻找一种更优雅的方法,将所有常见的参数都减少为一个DI参数。

我将Autofac用作DI容器(如果有任何区别)。

编辑:可能具有所有处理程序都从其继承并在该基类中的基类访问所有接口并将它们设置为基类的属性,但是我不知道该如何实现。

编辑2:Autofac进行了属性注入,但这似乎不是正确的方法,因此使用Mediatr的人们如何处理一遍又一遍的重复。我见过的每个使用Mediatr的开源项目,似乎都没有解决重复性问题。

最佳答案

当我发现自己有几个处理程序具有许多常见依赖项的情况时,我会看两件事:


我的经理人是否做得太多;和
如果是这样,我是否可以在单独的类中重构某些行为


例如,在您发布的处理程序代码中,有一个缓存客户端,这可能意味着您的处理程序做了两件事:


执行业务逻辑以检索硬币;和
进行一些逻辑运算会返回已经缓存的硬币,或缓存刚检索到的硬币


MediatR具有behaviors的概念,它使您可以在一个地方处理横切关注点。这可能适用于缓​​存,日志记录和异常处理。如果您熟悉ASP.NET Core中间件,则它们遵循相同的概念,因为每种行为都给出了:


当前请求(或MediatR语言中的查询);和
管道中的下一项,它可以是其他行为或查询处理程序


让我们看看如何提取行为中的缓存逻辑。现在,您无需遵循此示例到T,它实际上只是一种可能的实现。

首先,我们将定义一个接口,该接口适用于需要缓存的查询:

public interface IProvideCacheKey
{
    string CacheKey { get; }
}


然后,我们可以更改GetCoinByIdQuery来实现该新接口:

public class GetCoinByIdQuery : IRequest<CoinModel>, IProvideCacheKey
{
    public int Id { get; set; }

    public string CacheKey => $"{GetType().Name}:{Id}";
}


接下来,我们需要创建MediatR行为来处理缓存。这使用ASP.NET Core中提供的IMemoryCache仅仅是因为我不知道您的ICacheClient接口的定义:

public class CacheBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IProvideCacheKey, IRequest<TResponse>
{
    private readonly IMemoryCache _cache;

    public CacheBehavior(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;
    }
}


最后,我们需要向Autofac注册行为:

builder
    .RegisterGeneric(typeof(CacheBehavior<,>))
    .As(typeof(IPipelineBehavior<,>))
    .InstancePerDependency();


有了缓存,缓存就成为了一个跨领域的问题,它的实现位于单个类中,从而使其易于更改和测试。

我们可以将相同的模式应用于不同的事物,并使处理程序仅负责业务逻辑。

关于c# - Mediatr:减少DI对象的数量,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53966361/

10-12 04:49