0Pricing
C# Academy · Урок

Поведения конвейера

Добавляйте сквозную логику с помощью поведений.

«Поведения конвейера» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Сквозные задачи

Журналирование, проверка, измерение времени и транзакции применяются ко многим обработчикам. Копирование этого кода в каждый handler приводит к повторениям и ошибкам.

Поведения конвейера MediatR позволяют оборачивать каждый запрос общей логикой.

// One behavior runs around all handlers

Поведение конвейера

Поведение реализует IPipelineBehavior<TRequest, TResponse>. Его метод Handle получает запрос и делегат next, который вызывает оставшуюся часть конвейера.

public class LoggingBehavior<TRequest, TResponse>
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : notnull
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken ct)
    {
        // before
        var response = await next();
        // after
        return response;
    }
}

Следующий делегат

next() вызывает следующее поведение или фактический handler, если это последнее поведение. Код до вызова выполняется при прохождении запроса внутрь, а код после — при его выходе.

// [Behavior A in] -> [Behavior B in] -> [Handler]
//               <- [Behavior B out] <- [A out]

Поведение для журналирования

Конкретное поведение для журналирования записывает имя запроса при входе и затраченное время при выходе.

public async Task<TResponse> Handle(
    TRequest request,
    RequestHandlerDelegate<TResponse> next,
    CancellationToken ct)
{
    var name = typeof(TRequest).Name;
    _logger.LogInformation("Handling {Name}", name);
    var sw = Stopwatch.StartNew();
    var response = await next();
    _logger.LogInformation("{Name} took {Ms}ms", name, sw.ElapsedMilliseconds);
    return response;
}

Регистрация поведения

Зарегистрируйте открытое обобщённое поведение в DI. MediatR применит его к каждому запросу.

builder.Services.AddTransient(
    typeof(IPipelineBehavior<,>),
    typeof(LoggingBehavior<,>));

Поведение для проверки

Популярный вариант использования — проверка запросов с помощью FluentValidation. Поведение запускает все проверяющие компоненты до выполнения handler и выдаёт исключение, если хотя бы один из них сообщает об ошибке.

public async Task<TResponse> Handle(
    TRequest request,
    RequestHandlerDelegate<TResponse> next,
    CancellationToken ct)
{
    var context = new ValidationContext<TRequest>(request);
    var failures = _validators
        .Select(v => v.Validate(context))
        .SelectMany(r => r.Errors)
        .Where(f => f is not null)
        .ToList();
    if (failures.Count != 0)
        throw new ValidationException(failures);
    return await next();
}

Досрочное завершение

Поведение может полностью пропустить handler, вернув результат без вызова next(). Это полезно для кэширования результатов запросов или отклонения недопустимых запросов.

if (_cache.TryGet(key, out TResponse cached))
    return cached;          // handler never runs
var response = await next();
_cache.Set(key, response);
return response;

Порядок поведения

Поведения выполняются в порядке регистрации. Разместите журналирование снаружи, затем проверку, а затем транзакции, чтобы каждое поведение корректно оборачивало следующее.

services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(TransactionBehavior<,>));

Поведение транзакции

Оберните обработчики команд в транзакцию базы данных, чтобы вся операция атомарно фиксировалась или откатывалась.

await using var tx = await _db.Database.BeginTransactionAsync(ct);
try
{
    var response = await next();
    await tx.CommitAsync(ct);
    return response;
}
catch
{
    await tx.RollbackAsync(ct);
    throw;
}

Выбор определённых запросов

Используйте интерфейс-маркер и ограничение обобщённого типа, чтобы поведение применялось только к запросам, которые явно его подключают. Например, транзакции могут применяться только к командам.

public interface ITransactional { }
public record CreateOrderCommand(...) : IRequest<int>, ITransactional;

// where TRequest : ITransactional

Почему поведения лучше промежуточного ПО

В отличие от промежуточного ПО HTTP, поведения выполняются для каждого сообщения, получают типизированные запрос и ответ и работают даже тогда, когда запрос отправляется вне HTTP (например, из фоновой задачи).

// Strongly typed, transport-independent cross-cutting logic

Быстрая проверка

Проверьте, понимаете ли Вы принцип работы поведений.

Итоги

Вы изучили поведения конвейера:

  • IPipelineBehavior<TRequest, TResponse> оборачивает каждый запрос общей логикой.
  • Делегат next продвигает запрос по конвейеру; его пропуск досрочно завершает обработку handler.
  • Типичные применения: журналирование, проверка, транзакции и кэширование.
  • Поведения выполняются в порядке регистрации и строго типизированы.

Далее: уведомления и события.

Часто задаваемые вопросы

Урок «Поведения конвейера» бесплатный?

Да — полный текст урока «Поведения конвейера» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «Поведения конвейера»?

Добавляйте сквозную логику с помощью поведений. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Поведения конвейера»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Концепции CQRS
  2. Команды и обработчики с MediatR
  3. Поведения конвейера
  4. Уведомления и события
← Назад к C# Academy