C#怎么实现CQRS模式_C# MediatR命令查询分离教程【高级】
作者:归人云淡风轻
时间:2026-07-18
浏览:0
MediatR实现CQRS时,命令继承无泛型IRequest,查询继承IRequest;事务手动管理,DbContext用Scoped注入;INotification仅用于非关键异步通知;查询Handler通过构造函数注入DbContext并启用AsNoTracking(),避免直接new。
MediatR中命令与查询须严格分离:命令继承`IRequest`(无泛型),用`IRequestHandler`;查询继承`IRequest `,用`IRequestHandler `;事务需手动管理,`DbContext`必须Scoped注入;`INotification`仅用于非关键异步通知;查询严禁`new DbContext`,须构造注入并`AsNoTracking()`。

接口签名:命令和查询的“身份边界”
MediatR里,`IRequestHandler事务管理:别指望Send()自带原子性
一个常见的误解是:用了MediatR的`Send()`,命令执行和数据库提交就天然有了原子性。醒醒,`Send()`只是个管道调度器,事务得自己动手管。 最容易翻车的地方有两个:一是在Handler里直接`new DbContext()`,二是依赖注入的上下文没配对生命周期,导致事务跨Handler泄露或失效。 正确的做法并不复杂: - 注册`DbContext`时,老老实实用`AddDbContextINotification:是“广播”不是“命令通道”
新手容易把领域事件(比如`OrderCreatedNotification`)当成查询结果或命令副作用的标配通道,甚至幻想它能保证执行顺序或参与事务。但`INotification`本质上是个发布-订阅机制——无返回值、不保证顺序、默认异步fire-and-forget,典型的一次性通知。 所以它的适用范围很窄:非关键路径的衍生操作。比如发邮件、写日志、更新搜索索引。这些事情晚几秒甚至丢一两条,系统都能容忍。 有几个绝对不能做的事情: - 不要在`INotificationHandler`里修改聚合根或触发另一条命令。一旦这么干,流程就变得不可控,测试也成了噩梦,CQRS的边界形同虚设。 - 如果有强一致性的需求(比如“扣款成功后必须同步更新余额视图”),那应该由主命令的Handler显式调用写入服务,或者用事务性发件箱(Outbox pattern)来处理。依赖`MediatR.Publish()`是想多了。查询Handler里的new DbContext:隐蔽的性能冲击波
有人图省事,在查询Handler里直接写`using var context = new AppDbContext(...)`。看起来简单安全,实际上这是性能雷区——绕过DI生命周期管理,连接池滥用、上下文释放不及时、并发下状态污染,问题一串串。更隐蔽的是,它让查询享受不到EF Core的变更跟踪优化。比如同一请求内多次查同一个实体,Scoped上下文本来可以复用实例,结果每次都重新创建,白白浪费。 解决方案很直接: - 所有Handler一律通过构造函数注入`DbContext`,确保和请求生命周期一致。 - 查询类Handler要显式标记`AsNoTracking()`——除非你真的打算后续再Update。这一行代码能省掉大量内存和性能开销。 - 还有个常见低级错误:先`.ToList()`或`.Count()`拉数据到内存,再在内存里做LINQ过滤。EF Core没办法把后续操作转成SQL,最终全量加载,性能直接拉胯。 说到底,CQRS真正难的不是写几个Handler,而是守住命令与查询的物理隔离。数据库连接、缓存策略、异常处理粒度、监控指标埋点——通通要按角色区分对待。一旦在查询Handler里写了`Sa veChangesAsync()`,或者让命令返回DTO给前端渲染,CQRS就已经退化成普通分层架构了。
作者最新文章
NetLimiter安装配置方法及详细操作步骤教程
2026-10-09 15:54
win10怎么只关闭大版本升级保留安全更新推送
2026-10-06 12:00
3ds Max修改器堆栈顺序对模型的影响及调整技巧
2026-10-04 12:33
荣耀MagicOS 11发布计划与Agent Harness架构解析
2026-09-08 19:23
AI重构企业业务架构:超聚变“智企”范式核心解析
2026-09-08 18:39
热门文章
更多
精品专题
更多
Mac软件
更多
WINDOWS
更多










































