Dapper一对一映射需写对SQL、选准splitOn列、写好组装委托;SQL须用别名避免同名列冲突,splitOn填关联对象首列名(如"UserId"),委托中手动赋值并处理null。

用Dapper做一对一映射,核心就三点:写对SQL、选对splitOn列、写好组装委托。它不自动推断关系,但控制清晰、性能高,适合明确知道数据结构的场景。
SQL要包含两个表的字段,并注意列名不冲突
必须把主表和关联表的所有需要字段都查出来,推荐用表别名避免同名列覆盖(比如两个表都有Id):
SELECT p.Id AS PostId, p.Content, u.Id AS UserId, u.Name AS UserName
FROM Posts p
LEFT JOIN Users u ON u.Id = p.OwnerId
这样能确保Dapper按列顺序准确切分数据段。如果不用别名,且两表都有Id,Dapper可能误判分割点。
splitOn参数指定关联对象的起始列
splitOn不是写外键名,而是写“第二个对象字段在结果集里第一次出现的列名”。上例中User对象从UserId列开始,所以设为"UserId":
- 如果SQL里写的是
u.Id且没别名,那splitOn填"Id"(但容易和p.Id混淆) - 如果用了
u.Id AS UserId,就填"UserId"——更安全、更明确 - 大小写敏感,必须和SQL中AS后的名称完全一致
用委托函数把子对象挂到父对象属性上
Query方法返回的是主对象类型,嵌套对象靠你自己在委托里赋值:
var posts = connection.Query(sql,
(post, user) => {
post.Owner = user;
return post;
},
splitOn: "UserId"
);
关键细节:
-
post和user都是Dapper根据splitOn自动创建的实例 -
LEFT JOIN时user可能是null,建议在赋值前判断:post.Owner = user ?? new User();或保留null并用空合并操作符访问:post.Owner?.Name - 返回类型写
Post,表示最终列表是IEnumerable
实体类得有可写属性,且类型匹配
比如Post类里要有public User Owner { get; set; },不能是只读属性或私有字段。Dapper只映射public set属性,且不支持自动构造嵌套对象(不会调用new User())。
如果Owner属性类型和SQL中映射的User不一致(比如写成了object),运行时会抛异常,不是静默失败。
基本上就这些。不复杂但容易忽略别名和splitOn的对应关系。










