Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Many-many relationship in Entity Framework Code First and using the "virtual" keyword to access each other

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."

like image 873
Jan Carlo Viray Avatar asked Dec 05 '22 17:12

Jan Carlo Viray


1 Answers

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));
like image 93
Jakub Hromadík Avatar answered Dec 10 '22 10:12

Jakub Hromadík