0Pricing
C# Academy · 课时

管道行为

使用行为添加横切逻辑。

管道行为 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。

横切关注点

日志记录、验证、计时和事务适用于许多 handler。将这些代码复制到每个 handler 中既重复又容易出错。

MediatR 的流水线行为可以使用共享逻辑包装每个请求。

// One behavior runs around all handlers

IPipelineBehavior

行为会实现 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 委托

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();
}

短路

行为可以通过不调用 next() 直接返回,从而完全跳过 handler——这对于缓存查询结果或拒绝无效请求很有用。

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<,>));

事务行为

请在数据库事务中包装命令 handler,使整个操作能够以原子方式提交或回滚。

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 短路。
  • 常见用途包括:日志记录、验证、事务和缓存。
  • 行为按注册顺序运行,并且具有强类型。

下一步:通知和事件。

常见问题解答

「管道行为」课时是免费的吗?

是的 — 「管道行为」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。

「管道行为」这节课中我会学到什么?

使用行为添加横切逻辑。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「管道行为」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. CQRS 概念
  2. 使用 MediatR 处理命令和请求
  3. 管道行为
  4. 通知与事件
← 返回 C# Academy