没有链接表/模型的多对多关系?

Posted

技术标签:

【中文标题】没有链接表/模型的多对多关系?【英文标题】:Many-to-many relationships without link tables/models? 【发布时间】:2021-12-17 21:58:08 【问题描述】:

我正在创建一个 ASP.NET Web API 项目(首先是数据库),它从 MSSQL 数据库中提取数据(只读访问)。数据库有几个表,但没有主键/辅助键(我们无法更改)。我已经建立了一对多关系,没有任何问题,但是当涉及到多对多时,我不得不使用链接表来保存双方的键。

public class Student

    public int StudentId  get; set; 
    public string Name  get; set; 

    public IList<StudentCourse> StudentCourses  get; set; 


public class Course

    public int CourseId  get; set; 
    public string CourseName  get; set; 
    public string Description  get; set; 

    public IList<StudentCourse> StudentCourses  get; set; 

链接表:

public class StudentCourse

    public int StudentId  get; set; 
    public Student Student  get; set; 

    public int CourseId  get; set; 
    public Course Course  get; set; 

由于数据库中不存在链接表,我收到“Data.SqlClient.SqlException: 'Invalid object name 'StudentCourse'”错误。

public class SchoolContext : DbContext

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    
        optionsBuilder.UseSqlServer("Server=.\\SQLEXPRESS;Database=EFCore-SchoolDB;Trusted_Connection=True");
    

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    
        modelBuilder.Entity<StudentCourse>().HasKey(sc => new  sc.StudentId, sc.CourseId );
    
    
    public DbSet<Student> Students  get; set; 
    public DbSet<Course> Courses  get; set; 
    public DbSet<StudentCourse> StudentCourses  get; set; 

关系:

modelBuilder.Entity<StudentCourse>().HasKey(sc => new  sc.SId, sc.CId );

modelBuilder.Entity<StudentCourse>()
    .HasOne<Student>(sc => sc.Student)
    .WithMany(s => s.StudentCourses)
    .HasForeignKey(sc => sc.SId);


modelBuilder.Entity<StudentCourse>()
    .HasOne<Course>(sc => sc.Course)
    .WithMany(s => s.StudentCourses)
    .HasForeignKey(sc => sc.CId);

我考虑过在这些键上连接表,但它似乎不是处理关系和获取相关记录的有效方法。你会建议什么变通方法?

【问题讨论】:

“我考虑在这些键上连接表” 哪些键?你如何加入/链接一些没有在某处有一些共同列的东西?显示现有表的相关列可能有助于了解您想要实现的目标以及它是否可能(EF Core 通常不能容忍不正确的数据库设计)。 我已将我的 DbContext 添加到问题中,这正是我的问题,但我尝试使用替代名称来描述它。 上下文并不那么有趣,因为它显示了您如何尝试将类映射到......不确定是什么。在您所说的 OP 中,您正在使用无法更改的现有数据库,并且也没有链接表。所以我问您是否可以显示现有的 tables 及其相关的 columns 您的 DbContext 似乎定义了 StudentCourse 表的存在,但您说它在实际数据库中不存在。那么实际的数据库如何确定学生和课程之间的关系?你能告诉我们实际数据库的结构吗? 这就是问题所在,我在 db 中没有那个表,我想在这种情况下我不能把它放在 DBContext 中。应用程序是使用数据库优先的方法创建的,但是我们意识到我们必须添加一些东西,例如关系,就一对多而言,大小写相同,我没有在数据库中定义键,而是在 DBContext我已经写过这些表有键(并且没有迁移)。我搜索了一些资源,表明我们可以使用这种方法,因为应用程序使用 db 进行只读。 【参考方案1】:

Entity Framework 是一个 Object-to-Relational Mapper,Mapper 是这里的关键词。为了在要映射的对象之间建立关系,必须在关系数据库中的这些实体之间存在关系。

这样想,如果学生和课程之间存在关系,那么这种关系在您的数据库中是如何表示的?如果我要求您针对该数据库编写两个 SQL 查询,您将如何返回以下数据?

列出特定课程的所有学生。 列出特定学生的所有课程。

您不能仅使用 Course 和 Student 表来做到这一点。如果没有链接表,那么您要么以一种方式或另一种方式建立一对多关系,要么数据库以非关系方式处理它。 (例如 Student 包含一个字符串字段,其中包含以逗号分隔的课程 ID 列表)在这种情况下,EF 将无济于事。

如果数据库不支持记录学生和他们的课程之间的映射,您可以在其中查询一个学生如何参加许多课程,而每门课程可以有很多学生参加,那么 EF 无法配置为以某种方式自动读取并坚持这样的关系。数据库中必须存在多对多表,否则这种关系不能存在。对于 EF6 和 EF Core 5+,您可能会读到 EF 可以在没有链接 entity 的情况下处理多对多关系,这是真的,但这并不意味着没有链接 table时间>。该表必须存在,但您不需要定义 StudentCourse(或 CourseStudent)实体。

代替:

public IList<StudentCourse> StudentCourses  get; set; 

在学生和课程中,学生可以拥有:

public IList<Course> Courses  get; set; 

虽然课程有:

public IList<Student> Students  get; set; 

这仍然通过链接表进行映射,并且假设关系仅由表中的 StudentId 和 CourseId 组成,没有其他需要映射/配置的列,EF 可以完全在幕后管理该关系和表。

如果有一个链接表,只是没有按照您的期望命名:您可以随意调用实体,并通过属性、DbContext.OnModelCreating(ModelBuilder)EntityTypeConfiguration 使用配置将该实体和属性映射到现有的表和列,但是它们可能被命名。但必须存在这样的表才能支持这种关系。

【讨论】:

感谢@Steve Py 的详细回答,如果我使用joins(linq to sql) 来获取相关数据会怎样,在性能方面会不会有问题? 从如何使用 SQL 查询数据开始。您如何建议获取每个学生记录的课程记录和每个课程的学生记录?

以上是关于没有链接表/模型的多对多关系?的主要内容,如果未能解决你的问题,请参考以下文章

《Entity Framework 6 Recipes》翻译系列 -----第二章 实体数据建模基础之有载荷和无载荷的多对多关系建模 (转)

数据库优先方法中与联结表的多对多关系

SQLite中的多对多链接表外键建模

推进:没有交叉表的多对多关系

Hibernate的多对多关联关系

Hibernate的多对多关系