使用Dapper关联查询时Event列表未填充的问题排查
解决Dapper关联查询中Events列表未填充的问题
你的问题主要出在Dapper的splitOn参数配置和结果合并逻辑上,我来一步步帮你修正:
1. 修正splitOn参数的错误设置
你把splitOn写成了"Id, UserId",这不符合Dapper的规则。splitOn的作用是告诉Dapper:结果集中从哪一列开始,要映射到第二个实体(这里是Event)。你的查询结果里,u.Id属于User的属性,后续的e.UserId、e.EventText都是Event的属性,所以只需要把splitOn设为"UserId"就足够了。
2. 处理重复User的合并问题
Dapper的Query<User, Event, User>方法会为每一行返回一个User对象——也就是说,如果一个用户有3条Event记录,会返回3个相同Id的User,每个User的Events列表里只有一条Event。这时候需要我们手动合并相同Id的User,把所有关联的Event汇总到同一个列表中。
3. 处理LEFT JOIN返回的null Event
因为用了LEFT JOIN,没有关联Event的用户对应的e会是null,直接执行Add会报错,所以要先判断Event是否不为null再添加。
修正后的完整代码
// 补充查询User和Event的完整字段,确保对象属性映射完整 var sql = @"SELECT u.Id, u.Name, u.Department, e.Id AS EventId, e.UserId, e.EventText, e.StartTime, e.EndTime, e.Day, e.ColorIdentifier, e.Week FROM cpc.PLANNING_TOOL_USERS u LEFT JOIN cpc.PLANNING_TOOL_EVENTS e ON u.Id=e.UserId"; // 用字典跟踪已创建的User,避免重复实例化 var userDict = new Dictionary<int, User>(); SqlMapper.Query<User, Event, User>( connection, sql, (user, ev) => { // 如果字典里没有当前用户,初始化并加入字典 if (!userDict.TryGetValue(user.Id, out var existingUser)) { existingUser = user; existingUser.Events = new List<Event>(); userDict.Add(existingUser.Id, existingUser); } // 仅当Event不为null时添加(兼容无Event的用户) if (ev != null) { existingUser.Events.Add(ev); } return existingUser; }, splitOn: "EventId" // 用别名EventId区分User和Event的Id字段,避免映射混淆 ); // 最终得到的是所有用户,每个用户的Events列表已正确填充 var finalUsers = userDict.Values.ToList();
额外说明
- 我在SQL里补充了User和Event的完整字段,这样映射出来的对象属性才会完整,你可以根据实际需求调整查询字段。
- 用
EventId作为splitOn的列,是因为User和Event都有Id字段,用别名区分能避免Dapper映射时的混淆,这是个实用的小技巧。 - 借助字典跟踪已创建的User,确保同一个用户只被初始化一次,所有关联的Event都被归集到同一个列表中。
内容的提问来源于stack exchange,提问作者Bryan
相关产品推荐
相关产品推荐

