C# MongoDB关联集合查询带列表属性对象的实现方案
报错原因
原有代码无法正常执行有两个核心问题:
- MongoDB C# 驱动的LINQ翻译器不支持在
$project投影阶段直接使用new List<T>()的集合初始化语法,无法将该段C#代码翻译成MongoDB可识别的聚合管道语句,因此抛出对应异常。 - 普通
join是内连接逻辑,会返回分类与菜单项一一对应的笛卡尔积结果,即一个分类下有N个菜单项就会返回N条重复分类数据的记录,完全无法实现「一个分类嵌套对应多个菜单项」的一对多结构需求。
推荐实现方案
优先使用MongoDB原生$lookup聚合算子实现关联,兼容性最好,不会出现LINQ翻译兼容问题,代码如下:
var categoryCollection = database.GetCollection<CategoryDomainModel>("Categories"); var menuCollection = database.GetCollection<MenuItemDomainModel>("Menus"); // 1. 构造过滤条件:筛选指定餐厅的分类 var categoryFilter = Builders<CategoryDomainModel>.Filter.Eq(c => c.RestaurantID, restaurantID); // 2. 构建聚合管道 var categoriesAndMenus = categoryCollection.Aggregate() .Match(categoryFilter) // 关联Menus集合:本地关联键为分类_id,外键为菜单项的CategoryID,关联结果赋值给Menus字段 .Lookup<CategoryDomainModel, MenuItemDomainModel, CategoryAndMenusDomainModel>( foreignCollection: menuCollection, localField: c => c._id, foreignField: m => m.CategoryID, @as: res => res.Menus ) // 补全分类本身的字段映射 .Project(res => new CategoryAndMenusDomainModel { _id = res._id, RestaurantID = res.RestaurantID, CategoryName = res.CategoryName, Status = res.Status, Menus = res.Menus }) .ToList();
如果偏好LINQ语法,可以使用GroupJoin实现,注意不要手动初始化List,直接将分组结果转为列表即可,该写法在2.18及以上版本的MongoDB C#驱动中可正常翻译:
var categoriesAndMenus = (from category in categoryCollection.AsQueryable() where category.RestaurantID == restaurantID join menuItem in menuCollection.AsQueryable() on category._id equals menuItem.CategoryID into menuGroups select new CategoryAndMenusDomainModel { _id = category._id, RestaurantID = category.RestaurantID, CategoryName = category.CategoryName, Status = category.Status, Menus = menuGroups.ToList() }).ToList();
补充说明
- 如果需要对关联出来的菜单项做过滤(比如仅查询上架状态的菜品),可以使用带管道参数的Lookup重载,在关联阶段直接完成菜单项筛选,性能更优。
- 上述两种写法最终生成的MongoDB聚合管道逻辑一致,返回结果完全符合
CategoryAndMenusDomainModel的结构要求,不会出现分类重复、菜单项缺失的问题。
内容的提问来源于stack exchange,提问作者zvikow
相关产品推荐
相关产品推荐

