dapper 查询更快但易出错,因其不跟踪对象、不生成sql、无延迟加载,所有sql和映射需手动控制;常见错误包括参数名不匹配、空值未处理、事务未显式传参、异步上下文阻塞及多映射spliton字段名不一致等。

为什么 Dapper 查询比 Entity Framework 快但容易出错
因为 Dapper 不做对象跟踪、不生成 SQL、不自动处理延迟加载,所有 SQL 和映射都由你控制。快是真快,错也是真错——比如参数名拼错、类型不匹配、没处理空值,它不会提醒你,只会默默返回空或抛 SqlException。
常见错误现象:NullReferenceException 在读取结果时出现(实际是查询没返回数据,但代码直接调用 .First());InvalidOperationException: The parameterized query ... expects parameter '@xxx'...(参数名和 SQL 中不一致)。
- 用
QuerySingleOrDefault<t>()</t>代替QueryFirstOrDefault<t>()</t>如果业务上「必须有且仅有一个」,能提前暴露数据异常 - 所有参数一律用匿名对象或强类型类传入,避免用
IDictionary<string object></string>—— 容易键名大小写不一致 - 字符串参数别直接拼接进 SQL,哪怕只是固定值,也必须走参数化:写成
"WHERE Name = @name",而不是"WHERE Name = '" + name + "'"
Execute 和 Query 混用导致事务不生效
很多人以为只要开了 SqlTransaction,所有操作就自动参与事务。但 Dapper 的 Execute(增删改)和 Query(查)本身不感知事务上下文——除非你显式把 transaction 参数传进去。
使用场景:批量导入后校验,失败要整体回滚。
-
connection.Execute(sql, param, transaction: tx)——transaction参数不能漏 -
connection.Query(sql, param, transaction: tx)同理,即使只是 SELECT,也可能影响一致性判断(比如查中间状态) - 别依赖
connection.BeginTransaction()后的“隐式绑定”,Dapper不维护连接级事务上下文 - 如果用
using var tx = conn.BeginTransaction();,记得在tx.Commit()前所有 Dapper 调用都带transaction: tx
异步方法(QueryAsync)卡主线程?检查同步上下文
QueryAsync 看似异步,但在 WinForms 或某些 ASP.NET 同步上下文里,可能被同步阻塞,表现就是界面卡住或请求超时。根本原因不是 Dapper,而是 await 后默认会尝试捕获并回到原始上下文。
性能影响:在 Web API 中通常无感;在桌面应用或旧版 ASP.NET MVC 中极易触发死锁。
- Web API(.NET Core/5+)放心用
await connection.QueryAsync<t>(...)</t> - WinForms/WPF 中,建议加
.ConfigureAwait(false):await connection.QueryAsync<user>(sql, param).ConfigureAwait(false)</user> - 别在
async void方法里调用 Dapper 异步方法——异常无法被捕获 - 如果只是单条简单查询,同步
Query反而更稳,异步收益不大
复杂对象映射(一对多)写法不对,性能直接崩
Dapper 默认不支持自动展开关联集合(比如一个 Order 带多个 OrderItem),强行用多次查询或手动合并,很容易 N+1 或内存暴涨。
正确做法是用 Query<t u v></t> 多映射,配合 splitOn 指定分隔字段。
- SQL 必须用
JOIN一次性查出扁平结果集,不能靠循环查子表 -
splitOn: "ItemId"表示从哪一列开始算第二个类型(OrderItem)的字段 - 确保主表字段在前、从表字段在后,且从表字段名不能和主表重复(否则
splitOn找不准) - 如果关系是 1:N,需自己用
Dictionary<int list>></int>分组聚合,Dapper 不代劳
最常被忽略的是:splitOn 的字段名必须和 SQL 返回列名完全一致(区分大小写),而且不能是表达式别名如 o.Id AS OrderId —— 得写成 o.Id,否则映射失败也不报错,只返回 null 集合。











