Entity Framework Code First navigation property through relational table - c#

I'm trying to setup some navigation properties with some Entity Framework Code First models. I'd like them to look like this example:
public class Course
{
[Key]
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class Student
{
[Key]
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class StudentCourses
{
[Key, Column(Order = 0)]
public int StudentId { get; set; }
public virtual Student Student { get; set; }
[Key, Column(Order = 1)]
public int CourseId { get; set; }
public virtual Course Course { get; set; }
}
So Student and Course relationships would be established in the StudentCourses table. An instance of the student class would automatically reference all of that students courses, and vice versa an instance of the Course class would automatically reference all of its Students. And an instance of the StudentCourses class would automatically reference its Student and Course. But when I try to Update-Database, the relationships don't seem to get properly interpreted. Is there anything I'm missing here? Perhaps some configuring needs to be done in the context class? Most of the examples of navigation properties just show one-to-many relationship navigation properties.

You need to construct your models as shown below when you have a M : M relationship. You don't need to construct junction table. EF will create one for you when you do the data migration.
Model configuration using Conventions.
public class Student
{
public Student()
{
this.Courses = new HashSet<Course>();
}
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Your context class should be like this.
public class YourDBContext : DBContext
{
public YourDBContext () : base("Default")
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}

After following Sampath's advice, I actually decided I wanted to attach some other properties to the relationship. So I ended up defining the StudentCourses class afterall like this:
public class StudentCourses
{
[Key, Column(Order = 0)]
public int StudentId { get; set; }
public virtual Student Student { get; set; }
[Key, Column(Order = 1)]
public int CourseId { get; set; }
public virtual Course Course { get; set; }
public int Grade { get; set; }
}
So I changed Student and Course like this:
public class Course
{
[Key]
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<StudentCourses> Students { get; set; }
}
public class Student
{
[Key]
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual ICollection<StudentCourses> Courses { get; set; }
}
And most importantly, I did not add StudentCourses to the DbContext. So after Update-Database was performed, EF automatically created the table for StudentCourses, and the navigation properties all work.

Related

How to define common property for two different entities in EF core?

I have two entities Student and course as below
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
public virtual IList<Course> Courses { get; set; }
}
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<Student> Students { get; set; }
[ForeignKey(nameof(TeacherId))]
public int TeacherId {get;set;}
public Teacher Teacher { get; set; }
}
Now I want to add list of grades to two entities containing grade and id of the course or Student depending on the situation. Do I have to define a entity grade with studentId and CourseId or is there any other way to do it without creating entity
What you describe is a m:n-relationship between Course and Student with the extra information of the grade that was awarded for the participation. By creating the two navigation properties Student.Courses and Course.Students you have already created an implicit crosstab between the entities. In order to add the grade, I'd propose to create a dedicated entity, e.g. CourseParticipation that defines the relationship between Course and Student and also carries the extra information (up to now, the grade, later maybe more):
public class CourseParticipation
{
public int Id { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
public int StudentId { get; set; }
public Student Student { get; set; }
public int Grade { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
public virtual IList<CourseParticipation> Courses { get; set; }
}
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<CourseParticipation> Participants { get; set; }
[ForeignKey(nameof(TeacherId))]
public int TeacherId {get;set;}
public Teacher Teacher { get; set; }
}
This way, you make the relationship explicit and are prepared for later additions to the relationship.

Including related entities with a many-to-many relationship between

I have a many-to-many relationship set up with Entity Framework like this:
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public ICollection<StudentCourse> StudentCourses { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string Name { get; set; }
public ICollection<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; }
}
This works great, I have Students and I have Courses, and StudentCourses is the many-to-many relationship between them.
I also have an Advisor class, which has a collection of StudentCourses that the Advisor is in charge of:
public class Advisor
{
public int AdvisorId { get; set; }
public ICollection<StudentCourse> StudentCourses { get; set; }
}
I would like to get a collection of Advisors and the StudentCourses they're in charge of, but also the data properties from the Student and Course objects (like Name), all at once. This works for me:
var advisors = await _dbContext.Advisors
.Include(a => a.StudentCourses)
.ThenInclude(sc => sc.Student)
Include(a => a.StudentCourses)
.ThenInclude(sc => sc.Course)
.ToListAsync();
But is this the only way I can do that? Seems wrong to have that duplicate Include statement

Many to Many Relationship with extra columns in EF 6 Code?

Say if I have the classic example of Student and Courses. A student can have many courses and course can have many students.
How do I make the middle table in EF 6 code if I wanted to add an extra field on the many table part?
Do I just make in code another class and then hook it up somehow?
DB Context
public class MyContext : DbContext
{
public MyContext (string nameOrConnectionString) : base(nameOrConnectionString)
{
// this.Configuration.ProxyCreationEnabled = false;
Database.SetInitializer(new CreateDatabaseIfNotExists<OSPContext>());
}
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<StudentCourse> StudentCourses { get; set; }
}
public class StudentCourse
{
[Key]
public Guid StudentCourseId { get; set; }
public Guid StudentId { get; set; }
[ForeignKey("StudentId")]
public virtual Student Student { get; set; } // Include this so you can access it later
public Guid CourseId { get; set; }
[ForeignKey("CourseId")]
public virtual Course Course { get; set; }
public int Permissions { get; set; }
}
public class Course
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Student> Students { get; set; } = new >();
}
public class Student
{
[Key]
public Guid Id { get; set; }
public string Email { get; set; }
public virtual ICollection<Course> Courses { get; set; } = new List<Course>();
}
Given you are code first I would do something like the following.
public class Student
{
[Key]
public int StudentId { get; set; }
public string Name { get; set; }
public List<StudentCourse> Courses { get; set; } // So you can access Courses for a student
}
public class Course
{
[Key]
public int CourseId { get; set; }
public string Name { get; set; }
public List<StudentCourse> Students { get; set; }
}
public class StudentCourse
{
[Key]
public int StudentCourseId { get; set; }
public int StudentId { get; set; }
[ForeignKey("StudentId")]
public Student Student { get; set; } // Include this so you can access it later
public int CourseId { get; set; }
[ForeignKey("CourseId")]
public Course Course { get; set; }
}
EDIT: Wanted to note relationships are established with Data Attributes, you could also use EF Fluent API to establish your relationships. The properties will look the same, but without the [ForeignKey("")]

Entity Framework's one-to-many with buid-in type

I am a bit new to EntityFramework and faced the following problem:
I use virtual property to make lazy-load context.
public class Faculty
{
[Key]
public Guid FacultyIdentifier { get; set; }
public string FacultyAcronym { get; set; }
public virtual ICollection<Student> FacultyStudents { get; set; }
public virtual ICollection<Subject> FacultySubjects { get; set; }
public virtual ICollection<Course> FacultyCourses { get; set; }
public Faculty()
{
FacultyIdentifier = Guid.NewGuid();
FacultyStudents = new List<Student>();
FacultySubjects = new List<Subject>();
FacultyCourses = new List<Course>();
}
}
And it correctly creates 4 tables in database with PKs and FKs.
However, when I did the following:
[Table("CourseRecordsTable")]
public class Course
{
[Key]
public Guid CourseIdentifier { get; set; }
public virtual Faculty Faculty { get; set; }
public string SubjectCode { get; set; }
public virtual ICollection<Guid> AttendeeStudents { get; set; }
public Course()
{
CourseIdentifier = Guid.NewGuid();
AttendeeStudents = new List<Guid>();
}
}
Here it can't create another table for AttendeeStudents. I think that is happens because I didn't have defined class for Guid as it is build-in type. So, how can I solve this problem? In each Course there is list of attendee students : should I store them as ICollection of Student or as collection of Student's IDs (Guid)?

how to inherit the values of a foreign key in MVC 5? Invalid column name 'student_student_name'

I kept getting this error
Invalid column name 'student_student_name'
Here is my class for my database context
public class CSdbConnectionString : DbContext
{
public CSdbConnectionString()
: base("CSdbConnectionString")
{ }
public DbSet<appointment> appointments { get; set; }
public DbSet<faculty> faculties { get; set; }
public DbSet<sched> scheds { get; set; }
public DbSet<student> students { get; set; }
}
My appointment class needs to inherit the values of the foreign key faculty and student
[Table("appointment")]
public class appointment
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int appointment_id { get; set; }
public string appointment_description { get; set; }
public int student_id { get; set; }
public int faculty_id { get; set; }
public virtual student student { get; set; }
public virtual faculty faculty { get; set; }
}
Here is my faculty class
[Table("faculty")]
public class faculty
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int faculty_id { get; set; }
public string faculty_name { get; set; }
public string faculty_lname { get; set; }
public string faculty_email { get; set; }
public string faculty_password { get; set; }
public string faculty_dept { get; set; }
}
Here is my student class
[Table("student")]
public class student
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public string student_name { get; set; }
public string student_lname { get; set; }
public string student_email { get; set; }
public string student_password { get; set; }
public string student_number { get; set; }
public string student_program { get; set; }
}
The faculty class is working fine. I can inherit the values in my view
#item.faculty.faculty_name
I also used public virtual faculty faculty { get; set; } in another class to inherit the faculty values and it worked fine. I did the same thing to inherit the values in student class and it kept saying invalid column name.
I've already searched many solution here in stackoverflow and what happen is it just keeps getting new errors.
I tried adding this to the parent class which will be the one inherited
public virtual ICollection<appointment> appointments { get; set; }
and added this code
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<appointment>()
.HasRequired(c => c.student)
.WithMany(p => p.appointments)
.HasForeignKey(c => c.student_id);
}
and still there are errors.
All I did for the faculty to be inherited was add this to the child class public virtual faculty faculty { get; set; } and now I am trying to do the same with student and I always have errors. Can someone help me?
Well, first thing: Your student class has a string as a PrimaryKey and your are mapping it to an int in your appointment class
public int student_id { get; set; }
try to change it and see if it works.

Categories