C# 管道的用法

8 阅读2分钟

下面用“由浅入深 + 可运行代码”的方式,把 C# 里常见的 3 种管道(HTTP 管道 / MediatR 管道 / TPL Dataflow 管道) 一次讲透。
所有示例都基于 .NET 6/7 Minimal API,复制即可运行。


1. HTTP 管道(ASP.NET Core Middleware)

1.1 概念

  • 运行在 Kestrel 服务器 内部。
  • 每个 HTTP 请求 做横切逻辑(日志、鉴权、异常处理、压缩 …)。
  • 洋葱模型 组织:先注册的先执行外层,再进入内层,返回时反向再执行。

1.2 最小完整示例:全局计时 + 异常捕获

var builder = WebApplication.CreateBuilder();
var app = builder.Build();

// ① 计时中间件
app.Use(async (ctx, next) =>
{
    var sw = System.Diagnostics.Stopwatch.StartNew();
    await next();                 // 继续下一个中间件
    sw.Stop();
    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {ctx.Request.Path} 耗时 {sw.ElapsedMilliseconds} ms");
});

// ② 异常捕获中间件
app.Use(async (ctx, next) =>
{
    try { await next(); }
    catch (Exception ex)
    {
        ctx.Response.StatusCode = 500;
        await ctx.Response.WriteAsync($"Error: {ex.Message}");
    }
});

// ③ 业务端点
app.MapGet("/", () => "Hello Pipeline");
app.MapGet("/boom", () => throw new InvalidOperationException("boom!"));

app.Run();

运行后

  • / → 200 + 控制台打印耗时
  • /boom → 500 + Error: boom!

2. MediatR 管道(进程内业务级)

2.1 概念

  • 不是 HTTP 层,而是在你调用 IMediator.Send/ Publish 时生效。
  • 通过 IPipelineBehavior<TRequest,TResponse> 实现 验证、缓存、事务、日志 等横切。
  • 支持 多行为按注册顺序 包裹 Handler。

2.2 NuGet & 注册

dotnet add package MediatR
dotnet add package MediatR.Extensions.Microsoft.DependencyInjection
builder.Services.AddMediatR(cfg =>
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

2.3 完整示例:验证 + 缓存 + Handler

// ① 请求/响应
public record GetUserQuery(int Id) : IRequest<UserDto>;
public record UserDto(int Id, string Name);

// ② Handler
public class GetUserHandler : IRequestHandler<GetUserQuery, UserDto>
{
    public Task<UserDto> Handle(GetUserQuery req, CancellationToken ct)
    {
        Console.WriteLine("👉 Handler 被执行");
        return Task.FromResult(new UserDto(req.Id, "Alice"));
    }
}

// ③ 验证行为
public class ValidateBehavior<TReq, TRes> : IPipelineBehavior<TReq, TRes>
{
    public async Task<TRes> Handle(TReq req, RequestHandlerDelegate<TRes> next, CancellationToken ct)
    {
        if (req is GetUserQuery q && q.Id <= 0)
            throw new ArgumentException("Id 必须 > 0");
        return await next();
    }
}

// ④ 缓存行为(极简内存缓存)
public class CacheBehavior<TReq, TRes> : IPipelineBehavior<TReq, TRes>
{
    private static readonly ConcurrentDictionary<string, object?> _cache = new();
    public async Task<TRes> Handle(TReq req, RequestHandlerDelegate<TRes> next, CancellationToken ct)
    {
        var key = $"{typeof(TReq).Name}:{JsonSerializer.Serialize(req)}";
        if (_cache.TryGetValue(key, out var boxed)) return (TRes)boxed!;
        var res = await next();
        _cache[key] = res;
        return res;
    }
}

// ⑤ 注册顺序 == 执行顺序
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidateBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(CacheBehavior<,>));

// ⑥ 使用
app.MapGet("/user/{id:int}", async (int id, ISender mediatr) =>
{
    var user = await mediatr.Send(new GetUserQuery(id));
    return user;
});

控制台输出
第一次:👉 Handler 被执行
第二次:直接返回缓存结果,不再进入 Handler。


3. TPL Dataflow 管道(进程内数据流)

3.1 概念

  • 同一进程 内把任务拆成 块(Block)流水线并行
  • 适合 ETL、爬虫、图片处理 等 CPU/IO 密集型流水线。

3.2 NuGet & 最小示例:下载 → 转换 → 保存

dotnet add package System.Threading.Tasks.Dataflow
using System.Threading.Tasks.Dataflow;

// ① 定义 3 个块
var downloader = new TransformBlock<string, string>(url =>
{
    Console.WriteLine($"下载 {url}");
    Thread.Sleep(500);               // 模拟 IO
    return $"html-of-{url}";
});

var transformer = new TransformBlock<string, int>(html =>
{
    Console.WriteLine($"解析 {html}");
    return html.Length;
});

var saver = new ActionBlock<int>(len =>
{
    Console.WriteLine($"保存长度 {len}");
});

// ② 链接成管道
downloader.LinkTo(transformer, new() { PropagateCompletion = true });
transformer.LinkTo(saver, new() { PropagateCompletion = true });

// ③ 灌数据
foreach (var url in new[] { "a.com", "b.com", "c.com" })
    downloader.Post(url);

downloader.Complete();
await saver.Completion;   // 等待整个流水线结束

输出示例

下载 a.com
下载 b.com
下载 c.com
解析 html-of-a.com
保存长度 13
...

4. 总结速查

类型作用层关键字/接口典型场景
HTTP 管道Web 服务器UseMiddleware, UseWhen鉴权、日志、异常
MediatR 管道进程内业务IPipelineBehavior<T,R>验证、缓存、事务
Dataflow 管道进程内数据流TransformBlock, ActionBlockETL、爬虫、流水线

记住一句话:
HTTP 管请求MediatR 管业务Dataflow 管数据流