显然您有三个表: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,
});
如果您想获得由特定用户参加的课程(每个课程都有视频),您是否同意这看起来更自然?
奖励点:如果该用户参加的课程没有视频,您仍然会在结果中看到它们。您不会使用内部联接获得它们!