c# - 如果您不能从 C# Windows 应用程序(使用 Linq)使用自动生成的链接表,它的目的是什么?
问题描述
下面的代码,很好。有用。东西被插入,东西出现在左侧的右侧等等。但是,有一个问题。使用LinQPad ( https://www.linqpad.net ) 之类的程序,我可以query
查看数据并查看它的列表。
但是,尝试在 C# 中做同样的事情,表示上下文中不存在“自动”生成的链接表。
关系:多对多。
我正在使用Entity Framework 6。
编辑:如果您不能从 C# Windows 应用程序中使用自动生成的链接表,它的目的是什么?使用 Linq?
我的代码优先表:
public class Student
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int StudentID { get; set; }
public string StudentName { get; set; }
public DateTime? DateOfBirth { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CourseId { get; set; }
[Index("CourseName", 2, IsUnique = true)]
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
DBContext.cs
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany<Course>(s => s.Courses)
.WithMany(c => c.Students)
.Map(cs =>
{
cs.MapLeftKey("FK_StudentID");
cs.MapRightKey("FK_CourseID");
cs.ToTable("StudentCourse");
});
base.OnModelCreating(modelBuilder);
}
从 LinqPad 中查询,工作得非常好:
void Main()
{
var data = (from s in Students
join sc in StudentCourse on s.StudentID equals sc.FK_StudentID
join c in Courses on c.CourseId equals sc.FK_CourseID
select new { s,c });
Console.WriteLine(data);
}
解决方案
没有什么可以阻止您将其声明为实体,但是在链接表仅保留所需键的情况下,使用导航属性时不必声明它是一种优化。
我认为您在这里面临的关键问题是您正在编写类似于 SQL 的 Linq 并且完全缺少导航属性。
例如,如果我想要特定学生的所有课程: 如果没有链接表,我可以这样做:
var studentCourses = context.Students
.Where(x => x.StudentId == studentId)
.SelectMany(x => x.Courses)
.ToList();
要获得您概述的每个学生/课程组合的列表:
var studentCourses = context.Students
.SelectMany(s => s.Courses.Select(c => new {s, c}))
.ToList();
这给了我该学生的课程列表。我不必像编写 SQL 语句那样通过 StudentCourse 手动将学生加入课程。
现在您当然可以声明一个 StudentCourse 实体,但这会稍微改变关系。而不是多对多,你有一个多对一对多:
public class Student
{
// ...
public virtual ICollection<StudentCourse> StudentCourses { get; set; } = new List<StudentCourse>();
}
public class Course
{
// ...
public virtual ICollection<StudentCourse> StudentCourses { get; set; } = new List<StudentCourse>();
}
public class StudentCourse
{
[Key, Column(Order=0), ForeignKey(Student)]
public int StudentId { get; set; }
[Key, Column(Order=1), ForeignKey(Course)]
public int CourseId { get; set; }
public virtual Student Student { get; set; }
public virtual Course Course { get; set; }
}
将 StudentCourses 属性分别命名为“Courses”和“Students”可能很诱人,但 IMO 在浏览导航时会产生误导,正如我将在下面的示例中指出的那样。
对于那些使用 EFCore 的人,我相信这仍然是多对多唯一支持的选项。如果您想对连接表进行任何更改,例如使用专用的 PK 列或 CreatedAt 等其他列来跟踪关系的编辑等,这也是必需的选项。
然后要在 Linq 中执行第一个示例查询,您需要将其稍微更改为:
var studentCourses = context.Students
.Where(x => x.StudentId == studentId)
.SelectMany(x => x.StudentCourses.Course)
.ToList();
要使用此链接实体获取每个学生/课程组合的列表:
var studentCourses = context.StudentCourses.ToList();
// or, if there is other data in the linking entity and you just want student and course reference:
var studentCourses = context.StudentCourses
.Select(sc => new {sc.Student, sc.Course})
.ToList();
或者,您可以像在 Linqpad 中那样编写 Linq QL 语句,因为该实体已声明并且可以作为 DbSet 添加到上下文中。(如果您使用导航属性,则不需要 DbSet)
编辑:在下面添加示例:(我不能保证它的正确性,因为我几乎完全使用 Fluent Linq 语法,而不是 Linq QL)
var data = (from s in context.Students
join sc in context.StudentCourse on s.StudentID equals sc.StudentID
join c in context.Courses on c.CourseId equals sc.CourseID
select new { s,c });
这就是为什么我建议将 Student 上的属性命名为 StudentCourses 而不是 Courses。替代方案是:
.SelectMany(x => x.Courses.Course)
Courses 暗示我应该得到课程(因为避免链接实体的优化可以给你)但是你得到 StudentCourses 所以你留下它看起来很奇怪,因为 .Courses.Course 得到实际的课程。
推荐阅读
- python - 在 Django 中使用原始 SQL 打开事务是安全的
- python - Python - 从其他模块中的函数重新分配全局变量的最佳方法
- javascript - 如何在 PHP 中翻转字节数组
- django - NGINX docker-compose - 在上游 nuxt:3000 中找不到主机
- android - Android Studio 上未将手机检测为 Android 设备
- node.js - AZURE 不允许使用 HTTP 错误 405 方法
- android - 在 Android Studio 中 Flutter 运行失败原因:java.lang.IllegalStateException:缺少 Crashlytics 构建 ID
- javascript - 在 html 网页上显示列表 (JSON)
- python - 错误 FileNotFoundError [winError 2] anaconda
- azure - Azure CLI 无法连接到 Docker 守护程序