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

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

问题描述

我有很多命令和查询,其中大多数需要相同的接口来做不同的事情.是否有可能减少我的每个处理程序都需要并一遍又一遍地重复的这种混乱?

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.

编辑 2: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天全站免登陆