C# 手动实现CQRS模式方法 C#如何不依赖MediatR实现CQRS
发布时间 - 2026-02-02 00:00:00 点击率:次CQRS在C#中最简手动实现是通过分离ICommand/IQuery接口及对应处理器,命令只改状态无返回,查询只读数据不修改,类型不可变且职责明确。
什么是CQRS在C#里最简可行的手动实现
CQRS(Command Query Responsibility Segregation)本质是把“改数据”和“读数据”彻底拆开——不是靠框架,而是靠接口分离、类型隔离和调用路径隔离。不依赖 MediatR 时,核心就是自己定义 ICommand / IQuery 接口,再配两个独立的处理器抽象,不共享输入/输出模型,也不共用执行管道。
手动定义命令与查询的接口和处理器
关键不是写得多,而是分得清。命令不返回业务数据(只返回 void 或 Task),查询不修改状态(方法体里不能有 SaveChanges、Update 等)。所有类型都应显式声明职责:
-
ICommandHandler:只接受一个TCommand,无返回值 -
IQueryHandler:接受TQuery,必须返回TResult - 命令和查询类型本身是
record或不可变class,不继承、不带行为
示例:
public record CreateOrderCommand(string CustomerId, decimal Amount); public interface ICommandHandler{ Task Handle(TCommand command); } public class CreateOrderCommandHandler : ICommandHandler { private readonly OrderDbContext _db; public CreateOrderCommandHandler(OrderDbContext db) => _db = db; public async Task Handle(CreateOrderCommand command) { var order = new Order { CustomerId = command.CustomerId, Amount = command.Amount }; await _db.Orders.AddAsync(order); await _db.SaveChangesAsync(); } }
如何避免手写大量 if-else 或 switch 路由逻辑
不用 MediatR 就意味着没有自动泛型解析,但也不必硬写反射调度。推荐两种轻量方案:
- DI 容器直接注册具体处理器,按需注入——比如在 Controller 里明确构造
CreateOrderCommandHandler,不追求“统一路由” - 若真需要统一入口(如 API 层只暴露一个
Dispatch()),可用Dictionary静态缓存已注册的处理器实例,首次访问时通过Activator.CreateInstance构建并缓存,后续直接Cast调用 - 切忌在调度层做运行时类型判断 + 反射调用——性能差、堆分配多、调试困难
注意:IServiceProvider.GetService(Type) 可用,但必须确保该 Type 已在 DI 中注册为具体实现,否则返回 null 不报错,容易漏测。
查询侧容易忽略的隔离细节
很多人只拆了命令,查询仍用 EF 的 DbSet 直接暴露给 API,这等于没 CQRS。真正隔离要体现在三处:
- 查询 DTO 必须和实体类物理分离(不同命名空间、不同程序集更佳),禁止
select new OrderDto()之外的任何对实体的引用 - 查询 Handler 内部应使用
AsNoTracking(),且不复用命令侧的DbContext实例(哪怕同一请求周期) - 避免在查询中调用
Include()加载深层导航——
那是命令侧或领域服务的事;查询应只投射所需字段,用
SELECT x,y,z级别控制
一个典型错误是:在 GetOrderSummaryQueryHandler 里调用了 _db.Orders.Include(x => x.Items),结果无意中触发了延迟加载或全表扫描——这不是 CQRS,这是披着查询外衣的命令副作用。
# 处理器
# ai
# switch
# 路由
# c#
# 延迟加载
# Object
# NULL
# if
# 命名空间
# select
# include
# void
# 继承
# 接口
# 堆
# class
# 泛型
# 这是
# 加载
# 也不
# 那是
# 首次
# 两种
# 很多人
# 所需
# 得多
# 这不是
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
轻松掌握MySQL函数中的last_insert_id()
Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康
JS实现鼠标移上去显示图片或微信二维码
微信推文制作网站有哪些,怎么做微信推文,急?
5种Android数据存储方式汇总
如何用景安虚拟主机手机版绑定域名建站?
nodejs redis 发布订阅机制封装实现方法及实例代码
Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程
详解jQuery中基本的动画方法
JS去除重复并统计数量的实现方法
Win11搜索栏无法输入_解决Win11开始菜单搜索没反应问题【技巧】
JavaScript常见的五种数组去重的方式
详解Android中Activity的四大启动模式实验简述
极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?
Laravel怎么创建自己的包(Package)_Laravel扩展包开发入门到发布
Laravel如何配置和使用缓存?(Redis代码示例)
Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验
如何彻底删除建站之星生成的Banner?
如何确保FTP站点访问权限与数据传输安全?
Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】
Laravel安装步骤详细教程_Laravel环境搭建指南
Python3.6正式版新特性预览
Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】
微信小程序 scroll-view组件实现列表页实例代码
HTML透明颜色代码怎么让下拉菜单透明_下拉菜单透明背景指南【技巧】
免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?
如何批量查询域名的建站时间记录?
高防服务器租用首荐平台,企业级优惠套餐快速部署
Windows10电脑怎么设置虚拟光驱_Win10右键装载ISO镜像文件
Laravel如何配置.env文件管理环境变量_Laravel环境变量使用与安全管理
EditPlus中的正则表达式 实战(2)
Android实现代码画虚线边框背景效果
Laravel怎么做缓存_Laravel Cache系统提升应用速度的策略与技巧
敲碗10年!Mac系列传将迎来「触控与联网」双革新
详解Android——蓝牙技术 带你实现终端间数据传输
Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控
Laravel如何使用Service Container和依赖注入?(代码示例)
Python数据仓库与ETL构建实战_Airflow调度流程详解
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
Laravel用户认证怎么做_Laravel Breeze脚手架快速实现登录注册功能
Linux系统运维自动化项目教程_Ansible批量管理实战
如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南
网易LOFTER官网链接 老福特网页版登录地址
香港服务器网站卡顿?如何解决网络延迟与负载问题?
网站制作报价单模板图片,小松挖机官方网站报价?
如何在橙子建站上传落地页?操作指南详解
如何在云虚拟主机上快速搭建个人网站?
详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)
个人网站制作流程图片大全,个人网站如何注销?
如何在香港免费服务器上快速搭建网站?


