Dapper一对多查询时Product的StoreProducts子集合未填充如何解决
Dapper一对多关联查询子集合未填充问题解决
问题原因
- Dapper 原生的多映射
Query重载仅支持一对一关系绑定,不会自动聚合父对象的子集合属性 - 原有代码未手动给
Product.StoreProducts属性赋值,且该属性未做默认初始化,默认值为null - 原有映射逻辑中
StoreProduct.ProductId = Product.ProductId属于无效操作,关联查询返回的StoreProduct本身已携带正确的ProductId字段值,无需手动覆盖
解决方案
场景1:当前单条关联查询简化实现
你的查询条件已限定最多返回1条关联记录,直接在映射委托中初始化并填充子集合即可:
- (可选)先给
Product的StoreProducts属性添加默认初始化,避免空引用:
public class Product { public Guid ProductId { get; set; } public string Brand { get; set; } public string Model { get; set; } // 添加默认初始化 public virtual List<StoreProduct> StoreProducts { get; set; } = new List<StoreProduct>(); }
- 修改查询映射逻辑:
string sql = "SELECT * FROM StoreProduct AS A INNER JOIN Product AS B ON A.ProductId = B.ProductId WHERE A.StoreProductId = @StoreProductId and A.StoreId = @StoreId"; var connection = AppDbContext.Database.GetDbConnection(); return connection.Query<StoreProduct, Product, Product>( sql, (storeProduct, product) => { // 若未在实体中初始化,这里做兜底初始化 product.StoreProducts ??= new List<StoreProduct>(); // 将当前关联的StoreProduct添加到Product的子集合中 product.StoreProducts.Add(storeProduct); // 可选:维护双向关联,给StoreProduct的Product属性赋值 storeProduct.Product = product; return product; }, new { StoreProductId = storeProductId, StoreId = StoreID }, splitOn: "ProductId") .FirstOrDefault();
场景2:通用多结果一对多查询实现
如果查询会返回多个Product,每个Product对应多条StoreProduct,需要用字典做去重聚合,避免同一个Product被重复实例化:
string sql = "SELECT * FROM StoreProduct AS A INNER JOIN Product AS B ON A.ProductId = B.ProductId"; var connection = AppDbContext.Database.GetDbConnection(); var productDict = new Dictionary<Guid, Product>(); var productList = connection.Query<StoreProduct, Product, Product>( sql, (storeProduct, product) => { // 字典中不存在当前Product则存入 if (!productDict.TryGetValue(product.ProductId, out var existingProduct)) { existingProduct = product; existingProduct.StoreProducts = new List<StoreProduct>(); productDict.Add(existingProduct.ProductId, existingProduct); } // 关联StoreProduct到当前Product existingProduct.StoreProducts.Add(storeProduct); storeProduct.Product = existingProduct; return existingProduct; }, splitOn: "ProductId") .Distinct() // 去重返回唯一的Product集合 .ToList();
注意事项
- 泛型参数顺序需要和查询返回的列顺序完全匹配:你当前SQL先返回
StoreProduct的所有列,再返回Product的所有列,所以泛型顺序<StoreProduct, Product, Product>是正确的 splitOn参数指定的是第二个映射对象的主键列名,你当前配置为ProductId符合要求
内容的提问来源于stack exchange,提问作者João Cardoso
相关产品推荐
相关产品推荐

