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

如何通过单条LINQ查询将ObjectId映射到对应文档(ASP.NET Core+MongoDB)

问题

我正在使用ASP.NET Core和MongoDB开发在线学习应用,现有两个集合的DTO定义如下:

Course集合DTO

public class CourseDTO : DTOBase
{
    [BsonId] public ObjectId Id { get; init; }
    public required string Name { get; init; }
    public required string Description { get; init; }
    public required string ImageUrl { get; init; }
    public required ObjectId Teacher { get; init; }
    public ObjectId[] StudentIds { get; init; } = [];
}

User集合DTO

public class UserDTO : DTOBase
{
    [BsonId] public ObjectId Id { get; init; }
    public required string Username { get; init; }
    public required string PasswordHash { get; init; }
    public required string PasswordSalt { get; init; }
}

我需要实现一个方法,通过课程的ObjectId获取完整课程信息,同时将StudentIds数组映射为对应的用户详情。要求单次数据库请求完成,且保证类型安全。目前已实现课程基础字段和教师信息的查询,但学生部分尚未完成,现有代码如下:

现有查询方法

public async Task<CourseModel> Get(ObjectId id) 
{
    var query = from c in repo.Courses.AsQueryable()
                where c.Id == id
                join t in repo.Users.AsQueryable() on c.Teacher equals t.Id
                select new CourseModel 
                       {
                           Id = c.Id.ToString(),
                           Name = c.Name,
                           Description = c.Description,
                           ImageUrl = c.ImageUrl,
                           Teacher = new UserModel 
                                     {
                                         Id = t.Id.ToString(),
                                         Username = t.Username
                                     }
                           // todo: add students
                       };

    var course = await query.FirstOrDefaultAsync();

    if (course == null) 
        throw new NotFoundException("Course not found");

    return course;
}

模型定义

public class UserModel
{
    public required string Id { get; init; }
    public required string Username { get; init; }
}

public class CourseModel
{
    public required string Id { get; init; }
    public required string Name { get; init; }
    public required string Description { get; init; }
    public required string ImageUrl { get; init; }
    public required UserModel Teacher { get; init; }
    public UserModel[] Students { get; init; } = [];
}

我可以通过额外查询获取学生数据,但单次查询显然更高效。


解决方案

要实现单次查询关联学生数据,可利用MongoDB LINQ的关联查询能力(对应MongoDB的$lookup聚合阶段),将教师和学生的关联逻辑整合到一个查询中,同时保持类型安全。修改后的代码如下:

public async Task<CourseModel> Get(ObjectId id) 
{
    var query = from course in repo.Courses.AsQueryable()
                where course.Id == id
                // 关联教师用户
                join teacher in repo.Users.AsQueryable() on course.Teacher equals teacher.Id
                // 关联学生用户:匹配StudentIds中的所有用户ID
                join student in repo.Users.AsQueryable() on course.StudentIds.Contains(student.Id) equals true
                // 按课程分组,避免因多个学生导致课程数据重复
                group student by new { Course = course, Teacher = teacher } into courseGroup
                select new CourseModel 
                       {
                           Id = courseGroup.Key.Course.Id.ToString(),
                           Name = courseGroup.Key.Course.Name,
                           Description = courseGroup.Key.Course.Description,
                           ImageUrl = courseGroup.Key.Course.ImageUrl,
                           Teacher = new UserModel 
                                     {
                                         Id = courseGroup.Key.Teacher.Id.ToString(),
                                         Username = courseGroup.Key.Teacher.Username
                                     },
                           Students = courseGroup.Select(s => new UserModel 
                                 {
                                     Id = s.Id.ToString(),
                                     Username = s.Username
                                 }).ToArray()
                       };

    var course = await query.FirstOrDefaultAsync();

    if (course == null) 
        throw new NotFoundException("Course not found");

    return course;
}

说明

  1. 单次数据库请求:MongoDB会将上述LINQ查询转换为包含$match、$lookup和$group的聚合管道,仅向数据库发送一次请求,避免多次查询的性能开销。
  2. 类型安全:全程使用强类型的DTO和模型,编译阶段即可检查类型错误,无需手动拼接查询字符串。
  3. 处理空学生列表:如果课程没有学生,Students会自动初始化为空数组,符合CourseModel的默认值定义。

内容的提问来源于stack exchange,提问作者Thomas Renon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 06:36:09