简体   繁体   中英

Many-to-many relationships without link tables/models?

I am creating a ASP.NET web API project (database first) and it pulls the data from the MSSQL database (read-only access). Database have several tables but there is no primary/secondary keys (we cannot change it). I have set up of one-to-many relations without any problem, but when it comes to the many-to-many, I had to use link tables for holding keys from both side.

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; }
}

Link table:

public class StudentCourse
{
    public int StudentId { get; set; }
    public Student Student { get; set; }

    public int CourseId { get; set; }
    public Course Course { get; set; }
}

Because link table is not present in the database I am getting error of "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; }

Relationships:

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);

I considered Joining tables on these keys but it seems not efficient way of handling relationships and getting related records. What work arounds would you suggest?

Entity Framework is an Object-to-Relational Mapper, Mapper being the key term here. In order to have a relationship between objects to map, there must be a relationship between those entities in the relational database.

Think of it this way, if there is a relationship between students and courses, how is that relationship represented within your database? If I asked you to write two SQL queries against that database, how would you return the following data?

  • List all students for a specific Course.
  • List all courses for a specific Student.

You cannot do that with just a Course and a Student table. If there is no linking table then you either have a 1-to-many relationship one way or the other, or the database handles it in a non-relational way. (Such as Student containing a string field with a comma-delimited list of Course IDs) In which case, EF will be of little help.

If the database does not support recording a mapping between students and their courses where you can query how one student can participate in many courses, while each course can have many students participating, then EF cannot be configured to somehow auto-magically read and persist such a relationship. A many-to-many table must exist within the database or such a relationship cannot exist. With EF6 and EF Core 5+ you may read that EF can handle many-to-many relationships without a linking entity , which is true, but that does not mean without a linking table . The table must exist, but you don't need to define a StudentCourse (or CourseStudent) entity.

Instead of:

public IList<StudentCourse> StudentCourses { get; set; }

in both Student and Course, Student can have:

public IList<Course> Courses { get; set; }

while Course has:

public IList<Student> Students { get; set; }

This still maps through a linking table, and provided that relationship just consists of a StudentId and CourseId in the table with no other columns that need to be mapped/configured, EF can manage that relationship and table entirely behind the scenes.

If there is a linking table, just not named what you'd expect: You can call the entity whatever you want and use configuration via attributes, DbContext.OnModelCreating(ModelBuilder) , or EntityTypeConfiguration to map that entity and properties to the existing table and columns, however they might be named. But such a table must exist to support that relationship.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM