【问题标题】:convert Double Inner Join SQl query to LINQ将 Double Inner Join SQl 查询转换为 LINQ
【发布时间】:2020-01-13 02:28:43
【问题描述】:

我有一个 SQL 查询,想将其转换为 LINQ 语句,我之前从未使用过 Linq 语句,请帮助我解决这个问题,谢谢

SELECT c.* , v.*
  FROM UserEnrolleds u
  INNER JOIN Courses c ON u.CourseId = c.id
  INNEr JOIN Videos v ON v.CourseID = c.Id
  WHERE u.UsersID = '8851d572-eaff-4a84-9ec8-aa144fecfea2'

【问题讨论】:

    标签: sql entity-framework linq linq-to-sql


    【解决方案1】:

    显然您有三个表:Courses、Videos 和 UserEnrolleds。

    Courses 和 UserEnrolleds 之间存在关系,可能是一对多的关系:每个 Course 都有零个或多个 UserEnrolleds,每个 UserEnrolled 恰好属于一个 Course,即 Course.Id 为外键 UserEnrolled .CourseId 指向。

    类似地,视频和课程之间似乎存在一对多的关系:每个视频都有一个外键 Video.CourseId 到它所属的课程。

    在我看来,您想要由 ID 为“8851d572-eaff...”的用户注册的课程和视频

    您计划使用 UserEnrolleds - Courses - Videos 的内部联接来执行此操作。

    如果您使用实体框架,您可以使用两种方法。您可以使用virtual ICollection 属性,也可以自己加入。

    通常我发现ICollection 的使用更容易,也更有吸引力,但让我们首先关注您的问题。

    三个表的全内连接

    为此,我将使用Queryable.Join 的重载之一

    var result = dbContext.UserEnrolleds          // get table UserEnrolleds
    
        // keep only the UserEnrolleds with the mentioned UsersId:
        .Where(userEnrolled => userEnrolled.UsersId = "8851...")
    
        .Join(dbContext.Courses,                  // join with table Courses,
        userEnrolled => userEnrolled.CourseId,    // from every userEnrolled take the CourseId
        course => course.Id,                      // from every course take the Id
    
        (userEnrolled, Course) => new             // remember the matching items for the next join
        {
            UserEnrolled = userEnrolled,
            Course = Course,
        })
    
        .Join(dbContext.Videos,              // join with the Videos table
        joinResult => joinResult.Course.Id,  // from the previous Join take the Course Id
        video => video.CourseId,             // from the video take the CourseId
    
        (joinResult, video) => new           // when they match, make one new object
        {
           UserEnrolled => joinResult.UserEnrolled,
           Course => joinResult.Course,
           Video => video,
        })
    

    连接后,使用 Select 仅查询您实际计划使用的属性。

    .Select(joinResult => new
    {
        Course = new
        {
            Id = joinResult.Course.Id,
            Name = joinResult.Course.Name,
            ...
        },
    
        Video = new
        {
            Id = joinResult.Video.Id,
            Name = joinResult.Video.Name,
            ...
        }
    
        UserEnrolled = ...
    });
    

    如果要查询完整的课程和视频:

    .Select(joinResult => new
    {
         Course = joinResult.Course,
         Video = joinResult.Video,
    })
    

    请注意,您将传输几个您可能不会使用的属性,尤其是外键。

    当然,您可以在连接的最后一个参数(resultSelector)中进行选择。我没有这样做,以便更容易理解。

    使用虚拟ICollection

    通常,如果您有一对多的关系,您想要进行 GroupJoin 而不是 Join:您想要所有“课程及其视频”。

    所以不是表格:

    Course 1 - Video 10
    Course 1 - Video 11
    Course 1 - Video 12
    Course 2 - Video 13
    Course 2 - Video 14
    Course 3 - Video 15
    

    你想要一张桌子:

    Course 1 with its Videos 10, 11, and 12
    Course 2 with its Videos 13, and 14
    Course 3 with its one and only Video 15
    Course 4 has no Video at all.
    

    如果您更喜欢“带有视频的课程”(也许还喜欢带有 UserEnrolleds 的东西),使用virtual ICollection 比自己加入要容易得多。

    如果您关注entity framework code first conventions,您将获得类似于以下的课程:

    class Course
    {
        public int Id {get; set;}
        public string Name {get; set;}
        ...
    
        // Every Course has zero or more Videos:
        public virtual ICollection<Video> Videos {get; set;}
    
        // Every Course has zero or more UserEnrolleds:
        public virtual ICollection<UserEnrolled> UserEnrolleds {get; set;}
    }
    
    public class Video
    {
        public int Id {get; set;}
        public string Name {get; set;}
        ...
    
        // every Video belongs to exactly one Course, using foreign key:
        public int CourseId {get; set;}
        public virtual Course Course {get; set;}
    }
    

    UserEnrolled 类似于视频:

    public class UserEnrolled
    {
        public int Id {get; set;}
        public string UsersId {get; set;}
        ...
    
        // every Video belongs to exactly one Course, using foreign key:
        public int CourseId {get; set;}
        public virtual Course Course {get; set;}
    }
    

    在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系(一对多,多对多,...)

    外键是表中的真实列,因此它们是非虚拟的

    为了完整性,DbContext:

    class MyDbContext : DbContext
    {
        public DbSet<Course> Courses {get; set;}
        public DbSet<Video> Videos {get; set;}
        public DbSet<UserEnrolled> UserEnrolleds {get; set;}
    }
    

    这是实体框架检测表、表中的列以及它们之间的关系所需要知道的全部内容。它还将为您创建和使用主键和外键。

    现在获取由 ID 为“8851d572-eaff...”的用户注册的所有课程,每个课程及其视频(= 至少有一个 UserEnrolled 的用户 ID 等于“8851d572-eaff...” "),请使用以下查询:

    var coursesAndVideosEnrolledByUser = dbContext.Courses
    
        // keep only the Courses that are enrolled by user with Id "8851..."
        .Where(course => course.UserEnrolleds
                         .Any(userEnrolled => userEnrolled.UsersId = "8851d572-eaff..."))
        .Select(course => new
        {
            Id = course.Id,
            Name = course.Name,
            ...
    
            Videos = course.Videos,
        });
    

    如果您想获得由特定用户参加的课程(每个课程都有视频),您是否同意这看起来更自然?

    奖励点:如果该用户参加的课程没有视频,您仍然会在结果中看到它们。您不会使用内部联接获得它们!

    【讨论】:

      【解决方案2】:

      假设变量 db 是你的数据库尝试;

      var qdResult = 
          from u in db.UserEnrolleds 
              join c in db.Courses on u.CourseID equals c.id 
              join v in db.Videos on c.Id equals v.CourseID
          where u.UsersID = '8851d572-eaff-4a84-9ec8-aa144fecfea2'
          select c, v
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-20
        • 2017-07-12
        相关资源
        最近更新 更多