Entity Framework mapping between multiple tables - c#

I am trying to get the correct mapping between 4 tables.
MainTables
Class(Id, ClassName)
Course(Id, CourseName)
Student(Id, StudentName)
Relationship tables
ClassCourse(Id, ClassId, CourseId)
ClassCourseStudent(ClassCourseId, StudentId)
Class to Course has Many to Many mapping. So we use a relationship table ClassCourse to store the relationship
Student has one to Many mapping with ClassCourse.
So my question is how can I do the mapping for Student and ClassCourse
My code is
public class Class
(
public int Id {get;set;}
public string ClassName {get;set;}
public virtual ICollection<Course> Courses {get;set;}
)
public class Course
(
public int Id {get;set;}
public string CourseName {get;set;}
public virtual ICollection<Student> Students {get;set;}
)
public class Student
(
public int Id {get;set;}
public string StudentName {get;set;}
)
modelBuilder.Entity<Class>().ToTable("Class");
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Student>().ToTable("Student");
modelBuilder.Entity<Class>().HasMany(c => c.Courses).WithMany().Map(m => m.ToTable("ClassCourse")
m.MapLeftKey("ClassId")
m.MapRightKey("CourseId")
)
modelBuilder.Entity<Course>().HasMany(c => c.Students).WithMany().Map(m =>
m.ToTable("ClassCourseStudent")
m.MapLeftKey("ClassCourseId")
m.MapRightKey("StudentId")
The last mapping is the one I am looking for.
Thanks in advance.

I think you have to revisit your design. Right now you're trying to assign a composite key as foreign key, which can't be done.
What I would do is create a separate model that simply stores the course-class combination and provides a single key to reference. This will result in an extra table, but allows you to do what you want.
class Student {
public int StudentId {get; set;}
}
class Class {
public int ClassId {get; set;}
}
class Course {
public int CourseId {get; set;}
}
class ClassCourse {
public int ClassCourseId {get; set;}
public int ClassId {get; set;}
public int CourseId {get; set;}
}
Now every class should have a list of ClassCourse objects instead of Course, and every Course should have a list of ClassCourse objects. Now they're not directly linked together but are still connected trough an intermediate object and you can connect your Student objects to the primary key of ClassCourse.

Related

How to get records from table that depends on another tables in entity framework core?

I want to get some records from Database that depends on three tables.
Three tables are:
1.Company(Id,Name)
2. Car(Id,CompanyId,Name)
3. Showroom(Id,CarId,Name)
Now a one company contains many cars and many cars may exist in many showrooms.
I want to get records from showroom table where company '2' cars exist along with cars. Is it possible to do it in entity framework core?
I think your entities will be like :
Company
public class Company
{
public int Id {get; set;}
public string Name {get; set;}
public ICollection<Car> Cars {get; set;}
}
Car:
public class Car
{
public int Id{get; set;}
public string Name {get; set;}
public int CompanyId{get; set;}
public Company Company {get; set;}
}
ShowRoom:
public class ShowRoom
{
public int Id{get; set;}
public string Name {get; set;}
public int CarId{get; set;}
public Car Car{get; set;}
}
In your method:
var context = new SomeContext();
var showRooms= context.ShowRooms
.Include(x=> x.Car)
.ThenInclude(x=> x.Company)
.Where(x=> x.Car.Company.Id== 2)
.ToList();

Class for joined tables

I have two tables that I need to join
employee
-id
-name
-department_id
and
department
-id
-department_name
How can I handle the joined data in a single class? Should I create properties for all the data for each table?
e.g
class employeerecord
{
private int id {get; set;}
private string name {get; set;}
private int department_id {get; set;}
private string department_name {get; set;}
}
Right now, I'm using a datatable for viewing joined tables.
The whole design should include all entities you have in your database, i.e. there's departament table, there should be corresponding class. Same applies to employee table. You should have following classes:
class Employee
{
public int id {get; set;}
public string name {get; set;}
public int department_id {get; set;}
public Departament department {get; set;}
}
class Departament
{
public int id {get; set;}
public string name {get; set;}
//public ICollection<Employee> Employees { get; set; }
}
When loading data, you should wirte join query, to populate both: Employee instance and Departament instance.
You can uncomment commented line, if you'd like to have list of employees in particular departament.
This solely depends on what you want to do with this classes. You can go either way.
If you want to display a single employee and that employee can only belong to one department, then nothing speaks against a flat model:
class employeerecord
{
private int id{get;set};
private string name{get;set;}
private int department_id{get;set;}
private string department_name{get;set;}
}
If an employee can be a member of multiple Departments, you are better off storing the departments in a collection:
public class EmployeeViewModel
{
public int id{get;set};
public string name{get;set;}
public IEnumerable<Deparment> Departments { get; set; }
}
The same goes for departments. A department will most likely consist of multiple employees:
public class DepartmentViewModel
{
public int id{get;set};
public string name {get;set;}
public IEnumerable<Employee> Employees { get; set; }
}
And there is no reason, why you can't do all at once and use the classes depending on your specific use case.

Invalid column name error from SQL generated by EF

Below you can see the SQL should join by using [ClassId1] instead of [Class1_ClassId] since the latter doesn't exist.
I'm pretty sure I can use Fluent API to correct this but not sure what methods.
Generated SQL
SELECT ...
FROM [dbo].[School] AS [Extent1]
LEFT OUTER JOIN [dbo].[Student] AS [Extent2] ON [Extent1].[SchoolId] = [Extent2].[SchoolId]
LEFT OUTER JOIN [dbo].[Class] AS [Extent3] ON [Extent2].[Class1_ClassId] = [Extent3].[ClassId]
LEFT OUTER JOIN [dbo].[Class] AS [Extent4] ON [Extent2].[Class2_ClassId] = [Extent4].[ClassId]
WHERE ...
Tables
School
- SchoolId
- Name
- StudentId
Student
- StudentId
- Name
- Class1Id
- Class2Id
Class
- ClassId
- Name
Models
public class School
{
[Required]
public long SchoolId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public long StudentId { get; set; }
public virtual Student Student { get; set; }
}
public class Student
{
[Required]
public long StudentId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
}
public class Class
{
[Required]
public long ClassId { get; set; }
[Required]
public string Name { get; set; }
}
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
You haven't setup any relationship between these properties. Since you haven't defined foreign key columns for Class1 or Class2, it'll create them for you: Class1_ClassId and Class2_ClassId. Creating a migration should create those columns for you; but you'd end up with duplicates (Class1Id and Class1_ClassId for example).
I believe EntityFramework will resolve relationships between properties if the name ends with Id. Which means your setup should be:
[Required]
public long Class1Id { get; set; }
public long? Class2Id { get; set; }
public virtual Class Class1 { get; set; }
public virtual Class Class2 { get; set; }
However, I find it's better to be explicit - purely for readability and to ensure EF doesn't try to get too smart. I'd write it like this:
[Required]
public long ClassId1 { get; set; }
public long? ClassId2 { get; set; }
[ForeignKey("ClassId1")]
public virtual Class Class1 { get; set; }
[ForeignKey("ClassId2")]
public virtual Class Class2 { get; set; }
This should properly setup your foreign key relationships in the database.
I think Entity Framework constructed this SQL from your linq, because the relations between the classes in your model are unclear.
According to your model a School has only one Student, a Student doesn't know which School he attends, and is obliged to have one Class, and possibly a second one. A class does not know in which School it is, nor which Students are in the Class.
Are you sure about your model?
I'd gather that a School would have zero or more Students. A School also has zero or more Classes. Each class is a class in a School.
In database terms this is a typical one-to-many relationship. See Entity Framework Configure One-to-Many Relationship
Furthermore a Student attends zero or more Classes, a Class has one or more Students.
In database terms this is a typical many-to-many relationship. See: Entity Framework configure many-to-many relationship
These articles also describe schools, students and couses. Summarized the class definitions ought to be:
class School
{
public int Id {get; set;}
// a School has many Students:
public virtual ICollection<Student> Students {get; set;}
// a School has many Classes:
public virtual ICollection<Class> Classes {get; set;}
...
}
public class Student
{
public int Id {get; set;}
// A student belongs to one School via Foreign Key
public int SchoolId {get; set;}
public virtual School School {get; set;}
// A student attends many classes
public virtual ICollection<Class> Classes {get; set;}
...
}
class Class
{
public int Id {get; set;}
// a class belongs to one School via foreign key:
public int SchoolId {get; set;}
public virtual School School {get; set;}
// a class has many Students
public virtual ICollection<Student> Students {get; set;}
...
}
After this the DbContext will be as follows:
class MyDbContext : DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Class> Classes {get; set;}
}
If you model it like this, entity framework will automatically recognize the one-to-many relationships between School and Students, and create proper foreign keys for it. It will also recognize the many-to-many relationship between Students and Classes. It will even create a table for the many-to-many, which you won't need in your LINQ queries.
Entity Framework uses default conventions If you follow them, you won't need to tell the model about Table names and column names, about primary keys and foreign keys etc.
Back to your question
You want to tell your model that it should use a certain column name for a property instead of the column name it constructed from your class relations.
This can be done using Data Annotations within your class, or using Fluent API within your DbContext. I prefer using Fluent Api, because it allows you to use the same classes in different database structures without having to change the classes. If you want different table names, or different names for primary keys, different precision for decimals, etc, all you have to do is create a new DbContext. You don't have to change your classes, users of your classes won't notice the changes.
Fluent API is described here.
In you case: specify a table name instead of the default table name.
In my example, A Class would be put in table Classs, while of course you'd want it in table Classes:
class MyDbContext : DbContext
{
public DbSet<School> Schools {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Class> Classes {get; set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Entity Class should be put in table Classes
modelBuilder.Entity<Class>().ToTable("Classes");
// property Student.ClassId in column "ClassId1"
modelBuilder.Entity<Student>() // from class Student
.Property(student => student.ClassId) // take property ClassId
.HasColumnName("ClassId1"); // give it the column name "ClassId1"
}
}

How to return this result using linq in entity framework( 2 foreign key mapping table)

I have a table that contains 2 foreign key that reference separately to 2 different table.
I would like to return the result of all person that has course of "Science".
How to retrieve the record back using LINQ?
This is what i gotten so far:
return
_ctx.Person
.Include(u => u.Course
.Where(ug=>ug.CourseName== "Science"));
This is not working as it shows the error.
The Include path expression must refer to a navigation property
defined on the type
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<Person> Persons { get; set; }
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<Course> Courses { get; set; }
}
This is the mapping table. Only contains 2 foreign key from 2 different table.
I could not use this table inside the solution.As the code first won't generate this table as it doesn't contain it's own PK.
//This is not shown in the EntityFramework when generating Code First.
public class PersonCouseMap
{
public int PersonID {get; set;}
public int CourseID {get; set;}
}
Update : this works after I switched the entity.
return _ctx.Course
.Include(u=>u.Person)
.Where(ug=>ug.CourseName == "Sciene");
Anyone can explain why it won't work the another way round.
I need to display a List of Person who have course of "Science",
not Course Science that has a list of user.
The original query does not work because you've pushed the Where predicate inside the Include expression, which is not supported as indicated by the exception message.
The Include method is EF specific extension method used to eager load related data. It has nothing to do with the query filtering.
To apply the desired filter person that has course of "Science" you need Any based predicate since the Person.Courses is a collection:
return _ctx.Person
.Where(p => p.Courses.Any(c => c.CourseName == "Science"));
To include the related data in the result, combine it with Include call(s):
return _ctx.Person
.Include(p => p.Courses)
.Where(p => p.Courses.Any(c => c.CourseName == "Science"));
It looks like there is no relations between these two entites, you can establish a relationship by making the following changes to your code:
Here I am assuming that you want to establish Many-to-Many relationship between these two tables by having a third entity PersonCourseMap
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<CoursePersons> Courses { get; set; }
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<PersonCourse> Courses { get; set; }
}
public class PersonCourseMap
{
public int PersonID {get; set;}
public int CourseID {get; set;}
public virtual ICollection<Person> Persons { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
After making above changes you can simply navigate through properties.
Include Foreign Key Mapping
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<Person> Person {get; set}
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<Course> Course {get; set;}
}
using System.ComponentModel.DataAnnotation.Schema;
public class PersonCouseMap
{
[ForeignKey("Person")]
public int PersonID {get; set;}
[ForeignKey("Course")]
public int CourseID {get; set;}
public virtual ICollection<Person> Person {get; set;}
public virtual ICollection<Course> Course {get; set;}
}

Fluent Api: one to zero or one on both ends [duplicate]

This question already has answers here:
Is it possible to capture a 0..1 to 0..1 relationship in Entity Framework?
(3 answers)
Closed 7 years ago.
How do you configure a one to zero or one relationship one both sides. For example:
public class Student{
public int Id {get; set;}
public Registration Registration {get; set;}
}
public class Registration{
public int Id {get; set;}
//public int StudentId {get; set;}
public Student StudentEntity {get; set;}
}
A student can exist without a registration; and a registration can be created without a student. I am able to configure Registration like
HasOptional(o => o.StudentEntity).WithOptionalDependent(d => d.Registration ).Map(p => p.MapKey("StudentId"));
But this requires that I remove the StudentId property from my model. I however need this in order to update the relationship. How can I therefore configure such relationship and keep my foreignkey defined in the model?
The problem is in an one to one relationship both entities, the principal and the dependent, must share the same PK, and the primary key of the dependent also has to be the foreign key:
public class Principal
{
[Key]
public int Id {get;set;}
public virtual Dependent Dependent{get;set;}
}
public class Dependent
{
[Key,ForeignKey("Principal")]
public int PrincipalId {get;set;}
public virtual Principal Principal{get;set;}
}
There is not other way to map the FK in an one to one relationship in EF.
Seeing your model maybe what you really need is an one to many relationship.I think an student could be registered more than one time, in that case your model would be:
public class Student{
public int Id {get; set;}
public virtual ICollection<Registration> Registrations {get; set;}
}
public class Registration{
public int Id {get; set;}
public int StudentId {get; set;}
public Student StudentEntity {get; set;}
}
And the relationship configuration would be:
HasOptional(o => o.StudentEntity).WithMany(d => d.Registrations).HasForeignKey(o=>o.StudentId);

Categories