字段映射全为null主因是列名与属性名大小写不匹配,需用sql别名(如user_id as userid)或settypemap显式绑定;spliton须填第二个实体首字段的列别名,填错导致子表字段全null。

Query 为什么字段映射全为 null?
常见现象是 SQL 返回了数据,但 Order 和 User 的属性全是默认值(0、null),尤其在 PostgreSQL 或大小写敏感的 MySQL 中高频出现。
根本原因是 Dapper 默认按列名(不是别名)严格匹配属性名,且**大小写敏感**。比如数据库列是 user_id,而 C# 属性叫 UserId,不加处理就无法绑定。
- 最稳解法:SQL 中显式用
AS别名对齐属性名,例如SELECT user_id AS UserId, full_name AS FullName FROM users - 避免依赖
[Column]或[JsonProperty]—— Dapper 不识别这些特性 - 若不能改 SQL,可用
SqlMapper.SetTypeMap注册自定义映射器,但仅建议在全局统一场景下使用 - 别名必须唯一:两张表都有
Id字段时,必须分别写成o.Id AS OrderId和u.Id AS UserId,否则splitOn: "Id"会从第一个Id就切分,导致错位
splitOn 参数到底填什么?填错会怎样?
splitOn 不是填“第二个实体的类型名”,也不是填外键列名,而是填 SQL 结果集中**第二个实体第一个字段的列别名**。
例如 JOIN 查询返回字段顺序是:o.Id, o.Total, u.Id, u.Name,你想把后两个字段映射给 User,那么 u.Id 的别名(如 u.Id AS UserId)就是 splitOn 的值 —— 填 "UserId",不是 "Id" 或 "u.Id"。
- 填错最直接的表现:第二个实体所有字段都为 null,或第一个实体被截断(部分字段丢失)
- 三表 JOIN 时,
splitOn只支持单个字符串,不能传数组;需用QueryMultiple替代 - LEFT JOIN 可能导致第二个实体字段为 NULL,映射委托中记得判空:
(order, user) => { order.Customer = user ?? new User(); return order; }
一对多(如 Order + OrderItems)能不能用 Query 一次搞定?
不能。这是 Dapper 最常被误用的点 —— Query<order orderitem order></order> 在一对多场景下会生成大量重复 Order 实例(每条子记录一个),而不是把多个 OrderItem 聚合成一个 Order.Items 集合。
正确路径只有两条,取决于数据规模和结构:
- 小数据量(QueryMultiple 分两步查,再内存聚合
var sql = "SELECT * FROM Orders WHERE Id IN @ids; SELECT * FROM OrderItems WHERE OrderId IN @ids";
然后multi.Read<order>()</order>+multi.Read<orderitem>()</orderitem>,最后 foreach 关联 - 大数据量或需复用:改用字典缓存去重法 —— 在
Query的映射委托里维护Dictionary<int order></int>,遇到已存在的OrderId就复用对象并追加OrderItem - 严禁在循环里对每个 Order 单独查一次 Item —— N+1 查询会让性能崩掉
QueryMultiple 读取顺序错一位,会发生什么?
会直接抛 InvalidOperationException: The IDataReader has been disposed,或者后续 Read<t>()</t> 返回空集合 —— 因为 GridReader 内部指针是严格顺序推进的,没有 rewind 机制。
比如 SQL 是:"SELECT * FROM Users; SELECT * FROM Orders;",就必须先 reader.Read<user>()</user>,再 reader.Read<order>()</order>。反过来、跳过、或重复读同一类型,都会出问题。
- 必须用
using包裹QueryMultiple调用,否则连接不释放,很快耗尽连接池 - 参数只绑定一次,所有分号分隔的子查询共享同一组参数,所以
@id要在每个子查询里都出现,不能一个写@userId、另一个写@uid - MySQL 默认禁用多语句,连接字符串需加
Allow User Variables=true;PostgreSQL 不支持分号多语句,得换存储过程或客户端拼接
复杂关联的本质不是“怎么写更短”,而是“哪部分该在数据库做,哪部分该在内存做”。Dapper 把选择权交给你,但也意味着字段别名、读取顺序、空值处理这些细节,漏掉任何一个都可能让结果静默出错。











