You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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; 
        } 
    } 
}

错误原因分析

  1. SQL查询未关联菜单表:你写的select * from [dbo].[MenuCategories]只查了分类表,完全没拉取菜单数据,Dapper根本没法映射关联关系。
  2. splitOn参数无效:你设置了splitOn: "MenuID",但查询结果里根本没有MenuID这个字段,Dapper找不到拆分点,自然报错。
  3. 映射逻辑未处理一对多: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:02:07