用Dapper做一对一映射,核心就三点:写对SQL、选对
splitOn列、写好组装委托。它不自动推断关系,但控制清晰、性能高,适合明确知道数据结构的场景。
SQL要包含两个表的字段,并注意列名不冲突
必须把主表和关联表的所有需要字段都查出来,推荐用表别名避免同名列覆盖(比如两个表都有
Id): 示例SQL:
SELECT p.Id AS PostId, p.Content, u.Id AS UserId, u.Name AS UserName<br> FROM Posts p<br> 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<Post, User, Post>(sql,<br> (post, user) => {<br> post.Owner = user;<br> return post;<br> },<br> splitOn: "UserId"<br>);
关键细节:
post和
user都是Dapper根据
splitOn自动创建的实例
LEFT JOIN时
user可能是
null,建议在赋值前判断:
post.Owner = user ?? new User();或保留null并用空合并操作符访问:
post.Owner?.Name返回类型写
Post,表示最终列表是
IEnumerable<post></post>
实体类得有可写属性,且类型匹配
比如
Post类里要有
public User Owner { get; set; },不能是只读属性或私有字段。Dapper只映射public set属性,且不支持自动构造嵌套对象(不会调用new User())。
如果
Owner属性类型和SQL中映射的
User不一致(比如写成了
object),运行时会抛异常,不是静默失败。
基本上就这些。不复杂但容易忽略别名和splitOn的对应关系。
