将Double Inner Join SQl查询转换为LINQ

时间:2019-09-11 09:18:26

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

我有一个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'

2 个答案:

答案 0 :(得分:1)

显然您有三个表:“课程”,“视频”和“用户注册”。

课程与UserEnrolled之间存在关系,可能是一对多的关系:每个课程都有零个或多个UserEnrolled,每个UserEnrolled都完全属于一个课程,即带有Course.Id的课程,即外键UserEnrolled .CourseId指向。

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

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

您计划使用UserEnrolleds-课程-视频的内部联接来做到这一点。

如果使用实体框架,则可以使用两种方法。您可以使用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(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的UsersId等于“ 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,
    });

您是否同意,如果要让每个课程及其视频都由某个用户参加的话,这看起来自然吗?

加分点:如果该用户参加的课程没有视频,您仍然会在结果中得到它们。您将无法通过内部联接来获得它们!

答案 1 :(得分:0)

假设变量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