Медиатр: уменьшение количества DI-объектов - PullRequest
0 голосов
/ 29 декабря 2018

У меня много команд и запросов, и большинству из них нужны одинаковые интерфейсы, предназначенные для выполнения разных задач.Возможно ли каким-то образом уменьшить этот беспорядок, в котором нуждается каждый мой обработчик, и он повторяется снова и снова?

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, не решает проблему повторения.

1 Ответ

0 голосов
/ 29 декабря 2018

Когда я нахожусь в ситуации, когда несколько обработчиков имеют много общих зависимостей, я смотрю на 2 вещи:

  1. не слишком ли много делают мои обработчики;и
  2. , если это так, могу ли я провести рефакторинг некоторых действий в отдельном классе

Например, в опубликованном вами коде обработчика есть клиент кеша, которыйможет означать, что ваш обработчик делает 2 вещи:

  1. выполнение бизнес-логики для извлечения монеты;и
  2. выполняя некоторую логику, возвращают уже кэшированную монету или кэшируют только что полученную

MediatR имеет концепцию поведения , которая позволяет вам обрабатыватьсквозные проблемы в одном месте;это потенциально применимо к кешированию, ведению журналов и обработке исключений.Если вы знакомы с промежуточным программным обеспечением ASP.NET Core, они придерживаются той же концепции, что и каждое поведение:

  1. текущий запрос (или запрос на языке MediatR);и
  2. следующий элемент в конвейере, который может быть или другим поведением или обработчиком запроса

Давайте посмотрим, как мы можем извлечь логику кэширования в поведении.Теперь вам не нужно следовать этому примеру для 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, которое позаботится о кэшировании.При этом используется IMemoryCache, предоставляемый в ASP.NET Core, исключительно потому, что я не знаю определения вашего 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();

И вот, у нас это есть, кеширование - теперь междисциплинарная задача, реализация которой находится в одном классе, что делает его легко изменяемым и тестируемым.применять один и тот же шаблон для разных вещей и назначать обработчики ответственными только за бизнес-логику.

...