使用Dapper一对多关联时遭遇splitOn参数错误求助
解决Dapper Multimapping返回关联列表时的splitOn错误问题
嘿,我来帮你搞定这个Dapper的问题!你说尝试用Multimapping返回MenuCategories和关联的Menus时碰到了这个错误:
when using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id
先看看你的代码里的问题,再一步步解决:
你的相关类代码
VMMenuCategory.cs
public class VMMenuCategory { public int MenuCategoryID { get; set; } public string CategoryName { get; set; } public System.DateTime CreatedOn { get; set; } public DateTime? UpdatedOn { get; set; } public List<VMMenu> Menus { get; set; } }
VMMenu.cs
public class VMMenu { public int MenuID { get; set; } public int MenuCategoryID { get; set; } public string ProductName { get; set; } public int? CostPrice { get; set; } public int? SellingPrice { get; set; } public System.DateTime CreatedOn { get; set; } public DateTime? UpdatedOn { get; set; } }
原来的MenuCategoriesRepository.cs代码
public sealed class MenuCategoryRepository : Connection, IMenuCategoryRepository { List<VMMenuCategory> IMenuCategoryRepository.GetAllMenuCategories() { List<VMMenuCategory> _lstVMMenuCategory = new List<VMMenuCategory>(); string query = "select * from [dbo].[MenuCategories]"; using (var connection = GetConnection()) { var data = connection.Query<VMMenuCategory, VMMenu, VMMenuCategory>(query, map:(mc,m) => { mc.Menus =new List<VMMenu>() ;return mc; },splitOn: "MenuID").ToList(); return data; } } }
错误原因分析
- SQL查询未关联菜单表:你写的
select * from [dbo].[MenuCategories]只查了分类表,完全没拉取菜单数据,Dapper根本没法映射关联关系。 - splitOn参数无效:你设置了
splitOn: "MenuID",但查询结果里根本没有MenuID这个字段,Dapper找不到拆分点,自然报错。 - 映射逻辑未处理一对多:Dapper的
Query默认是一对一映射,你现在的写法只是给每个分类初始化了空菜单列表,没把查询到的菜单添加进去,就算查了关联数据也没用。
解决方法
1. 修正SQL查询,添加关联查询
用LEFT JOIN关联菜单表(这样就算分类没有菜单也能返回),还要给同名字段(比如CreatedOn、UpdatedOn)起别名,避免映射混淆:
SELECT mc.MenuCategoryID, mc.CategoryName, mc.CreatedOn, mc.UpdatedOn, m.MenuID, m.MenuCategoryID, m.ProductName, m.CostPrice, m.SellingPrice, m.CreatedOn AS MenuCreatedOn, m.UpdatedOn AS MenuUpdatedOn FROM [dbo].[MenuCategories] mc LEFT JOIN [dbo].[Menus] m ON mc.MenuCategoryID = m.MenuCategoryID
2. 修正Dapper映射逻辑,处理一对多关联
我们需要用一个字典来跟踪已经创建的分类,避免重复生成分类对象,同时把对应的菜单添加到分类的列表里:
public sealed class MenuCategoryRepository : Connection, IMenuCategoryRepository { List<VMMenuCategory> IMenuCategoryRepository.GetAllMenuCategories() { // 用字典跟踪已创建的分类,key是分类ID var categoryDictionary = new Dictionary<int, VMMenuCategory>(); // 修正后的SQL查询 string query = @"SELECT mc.MenuCategoryID, mc.CategoryName, mc.CreatedOn, mc.UpdatedOn, m.MenuID, m.MenuCategoryID, m.ProductName, m.CostPrice, m.SellingPrice, m.CreatedOn AS MenuCreatedOn, m.UpdatedOn AS MenuUpdatedOn FROM [dbo].[MenuCategories] mc LEFT JOIN [dbo].[Menus] m ON mc.MenuCategoryID = m.MenuCategoryID"; using (var connection = GetConnection()) { connection.Query<VMMenuCategory, VMMenu, VMMenuCategory>( query, (category, menu) => { // 检查字典里是否已有该分类 if (!categoryDictionary.TryGetValue(category.MenuCategoryID, out var existingCategory)) { existingCategory = category; existingCategory.Menus = new List<VMMenu>(); categoryDictionary.Add(existingCategory.MenuCategoryID, existingCategory); } // 如果菜单不为null(LEFT JOIN可能返回无菜单的情况),添加到分类的菜单列表 if (menu != null) { existingCategory.Menus.Add(menu); } return existingCategory; }, splitOn: "MenuID" // 告诉Dapper从MenuID字段开始拆分,后面的字段映射到VMMenu ); } // 返回字典里的所有分类 return categoryDictionary.Values.ToList(); } }
补充说明
- 为什么用字典?因为JOIN后的结果集里,一个分类会对应多条记录(每个菜单一条),如果不用字典,你会得到多个重复的分类对象,每个只带一个菜单,而不是一个分类包含所有关联菜单。
splitOn: "MenuID"现在生效了,因为查询结果里确实有MenuID字段,Dapper会把MenuID及之后的字段映射到VMMenu对象,之前的字段映射到VMMenuCategory。
内容的提问来源于stack exchange,提问作者Tejinder Singh
相关产品推荐
相关产品推荐

