Mediatr:减少DI对象的数量 [英] Mediatr: reducing number of DI'ed objects

查看:147
本文介绍了Mediatr:减少DI对象的数量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有很多命令和查询,并且大多数命令和查询都需要相同的接口DI'ed才能执行不同的操作.是否可以某种方式减少我的每个处理程序所需的杂乱无章,并一遍又一遍地重复?

I have a lot of commands and queries and most of them need same interfaces DI'ed to do different things. Is it possible to some how reduce this clutter that each and every one of my handler needs and it is repeated over and over?

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的参数.

This may not be directly related to Mediatr but I am looking for a more elegant way of just reducing all the common ones to maybe ONE DI'ed param.

如果有任何不同,我将使用Autofac作为我的DI容器.

I am using Autofac as my DI container if it makes any difference.

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

possibly having base class that all the handlers inherit from and in the base class get access to all the interfaces and set them as properties on the base class, but I have no idea how to achieve this.

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

EDIT 2: Autofac has property injection but that seems like it is not the right approach, so people who are using Mediatr, how are you handling of repeating yourself over and over. Every open source project that uses Mediatr that I have seen, seem to not address the repeating yourself problem.

推荐答案

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

When I find myself in the situation where several handlers have many common dependencies, I look at 2 things:

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

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

As an example, in the handler code you posted, there's a cache client, which could possibly mean your handler does 2 things:

  1. 执行业务逻辑以检索硬币;和
  2. 做一些逻辑确实会返回已经缓存的硬币,或缓存刚取回的硬币

MediatR具有行为的概念,可让您处理跨领域的问题在一个地方这可能适用于缓​​存,日志记录和异常处理.如果您熟悉ASP.NET Core中间件,则它们遵循相同的概念,因为给出了每种行为:

MediatR has the concept of behaviors which allow you to handle cross-cutting concerns in a single place; this is potentially applicable to caching, logging and exception handling. If you're familiar with ASP.NET Core middlewares, they follow the same concept, as each behavior is given:

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

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

Let's see how we could extract the caching logic in a behavior. Now, you don't need to follow this example to a T, it's really just one possible implementation.

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

First, we'll define an interface that we apply to queries that need to be cached:

public interface IProvideCacheKey
{
    string CacheKey { get; }
}

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

Then we can change GetCoinByIdQuery to implement that new interface:

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

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

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

Next, we need to create the MediatR behavior that will take care of caching. This uses IMemoryCache provided in ASP.NET Core solely because I don't know the definition of your ICacheClient interface:

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注册行为:

Lastly, we need to register the behavior with Autofac:

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

我们有了它,缓存现在是一个贯穿各领域的问题,其实现位于单个类中,从而使其易于更改和测试.

And there we have it, caching is now a cross-cutting concern, which implementation lives in a single class, making it easily changeable and testable.

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

We could apply the same pattern for different things and make the handlers only responsible for business logic.

这篇关于Mediatr:减少DI对象的数量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆