Many-many relationship in Entity Framework Code Fi

2019-02-14 06:40发布

This excerpt code successfully creates a many-many relationship with an explicit Junction table that has additional data within it.

PROBLEM: I want to be able to access Courses from Student and vice versa,
(therefore the commented virtual property. But if I uncomment it, it causes an error (see below))

If I don't explicitly create a junction table (no additional data), the virtual keyword works though as EF creates a junction table by convention.

QUESTION:

How can I make Student access Courses without going through Enrollments? Or is that not possible? If it's not possible, then what's the best way to go about this?

(a beginner in EF and C#)

    public class Student
    {
        [Key]
        public int StudentId { get; set; }
        public string StudentName { get; set; }

        //public virtual Course Courses { get; set; }
    }

    public class Course
    {
        [Key]
        public int CourseId { get; set; }
        public string CourseName { get; set; }

        //public virtual Student Students { get; set; }
    }

    public class Enrollment
    {
        [Key]
        public int EnrollmentId { get; set; }
        public Student Student { get; set; }
        public Course Course { get; set; }
        public string Grade { get; set; }
    }

    public class ManyMany : DbContext, IContext
    {
        public DbSet<Student> Students { get; set; }
        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }

        public void Run()
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<ManyMany1>());
            this.Courses.Add(new Course() {CourseName = "English"});
            this.SaveChanges();
        }
    }

WHEN I UNCOMMENT public virtual...

ERROR: "Unable to determine the principal end of an association between the types 'EF.Course' and 'EF.Student'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations."

2条回答
Rolldiameter
2楼-- · 2019-02-14 07:11

Entity Framework can't automatically determine 'many-to-many' relations because they are expressed with the help of additional tables in SQL (in your case it is Enrollment table). You can specify mappings directly in OnModelCreating method:

public class YourDbContext : DbContext
{
    ....

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Student>().HasMany(x => x.Courses).WithMany(x => x.Students)
            .Map(m =>
            {
                m.ToTable("Enrollment"); // Relationship table name
                m.MapLeftKey("StudentID"); // Name of column for student IDs
                m.MapRightKey("CourseID"); // Name of column for course IDs
            });
    }
}

Also, take a note that if an entity have many other entities, use collection for relationship:

public class Student
{
    ....
    public virtual ICollection<Course> Courses { get; set; } // Many courses
}

public class Course
{
    ....
    public virtual ICollection<Student> Students { get; set; } // Many students
}
查看更多
何必那么认真
3楼-- · 2019-02-14 07:21
public class Student
{
    public virtual int StudentId { get; set; }
    public virtual string StudentName { get; set; }

    public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class Course
{
    public virtual int CourseId { get; set; }
    public virtual string CourseName { get; set; }

    public virtual ICollection<Enrollment> Enrollments { get; set; }
}

public class Enrollment
{
    public virtual int StudentId { get; set; }
    public virtual int CourseId { get; set; }
    public virtual string Grade { get; set; }

    public virtual Student Student { get; set; }
    public virtual Course Course { get; set; }
}

public class ManyMany : DbContext
{
    public DbSet<Student> Students { get; set; }
    public DbSet<Course> Courses { get; set; }
    public DbSet<Enrollment> Enrollments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Student>()
            .HasKey(student => student.StudentId);
        modelBuilder.Entity<Course>()
            .HasKey(course => course.CourseId);
        modelBuilder.Entity<Enrollment>()
            .HasKey(enrollment => new { enrollment.StudentId, enrollment.CourseId } );

        modelBuilder.Entity<Student>()
            .HasMany(student => student.Enrollments)
            .WithRequired(enrollment => enrollment.Student)
            .HasForeignKey(enrollment => enrollment.StudentId);
        modelBuilder.Entity<Course>()
            .HasMany(course => course.Enrollments)
            .WithRequired(enrollment => enrollment.Course)
            .HasForeignKey(enrollment => enrollment.CourseId);
    }
}

Older question concerning this, answer and more info here: Entity Framework CodeFirst many to many relationship with additional information.

EDIT: Usage example:

    var context = new ManyMany();

    var physicsCourse = new Course() { CourseName = "Physics" };
    var mathCourse = new Course() { CourseName = "Math" };

    var studentJohn = new Student() { StudentName = "John Doe" };
    var studentJane = new Student() { StudentName = "Jane Doe" };

    var physicsCourseEnrollmentJohn = new Enrollment() { Student = studentJohn, Course = physicsCourse };
    var mathCourseEnrollmentJohn = new Enrollment() { Student = studentJohn, Course = mathCourse };
    var physicsCourseEnrollmentJane = new Enrollment() { Student = studentJane, Course = physicsCourse };

    context.Courses.Add(physicsCourse);
    context.Courses.Add(mathCourse);
    context.Students.Add(studentJohn);
    context.Students.Add(studentJane);

    studentJohn.Enrollments.Add(physicsCourseEnrollmentJohn);
    studentJohn.Enrollments.Add(mathCourseEnrollmentJohn);
    studentJane.Enrollments.Add(physicsCourseEnrollmentJane);

    physicsCourse.Enrollments.Add(physicsCourseEnrollmentJohn);
    mathCourse.Enrollments.Add(mathCourseEnrollmentJohn);
    physicsCourse.Enrollments.Add(physicsCourseEnrollmentJane);

    context.Enrollments.Add(physicsCourseEnrollmentJohn);
    context.Enrollments.Add(mathCourseEnrollmentJohn);
    context.Enrollments.Add(physicsCourseEnrollmentJane);

    context.SaveChanges();

    var johnsEnrollments = context.Students.Where(student => student.StudentId == studentJohn.StudentId).Single().Enrollments;
    MessageBox.Show(string.Format("Student John has enrolled in {0} courses.", johnsEnrollments.Count));
    var janesEnrollments = context.Students.Where(student => student.StudentId == studentJane.StudentId).Single().Enrollments;
    MessageBox.Show(string.Format("Student Jane has enrolled in {0} courses.", janesEnrollments.Count));
查看更多
登录 后发表回答