If I have a Course class, that has a collection of students (ICollection<Person>) as follows:
public class Person
{
public Person()
{
this.Courses = new HashSet<Course>();
}
public int PersonId { get; set; }
[Required]
public string PersonName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Person>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Person> Students { get; set; }
}
I end up with this structure in the database (as expected):
(note the PersonCourses table)
However, in my example, I also want to add an instructor to the course.
This instructor is also a Person, who can attend courses just like everyone else, so I adjust the above classes as shown below:
public class Person
{
public Person()
{
this.Courses = new HashSet<Course>();
}
public int PersonId { get; set; }
[Required]
public string PersonName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<Course> InstructedCourses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Person>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Person> Students { get; set; }
public virtual Person Instructor { get; set; }
}
What I was expecting to see is the same database structure as above, but with an additional table created that linked a person to many courses.
However, what I got was this:
(Note that the PersonCourses table has gone)
What I was Expecting/Hoping to see was similar to this:
It's probably worth stating that the reason I've not got a separate Instructor/Person class is that I'm expecting that any Person can create a course, and thus become an instructor for that course.
Firstly - Is this possible to achieve via code-first in EF? I'm assuming so..
Secondly - What is it I'm doing wrong?
Thirdly - Is it the weekend yet?
All help appreciated :)
This is one reason I don't like / recommend code-first. It looks like EF got confused with the second InstructedCourses collection and instead just set up the instructor reference back from the course, though it seems to have just made the students collection a 1-to-many as well.
I would seriously consider either:
A) changing you domain to define an Instructor entity vs. Student entity
or
B) Do schema first with the proper EF mappings to the tables you want.
I don't think any DBA is going to want to see things like course_personId / Person_personId throughout the schema that they are one day going to need to support and optimize.
Instructors and Students can extend a base "Person" class with either table per entity or an identifier. Course to instructor and course to student relationships can then be defined more clearly. The limitation would be if you wanted the same "person" to be able to be referenced as both an instructor and a student.
Related
I'm using entity framework code first approach
I have a class
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
public Person Director { get; set; }
public virtual ICollection<Person> Actors { get; set; }
}
and a class
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
When the database is created I get one table Movies with Id, Title, Director_Id and a table Person with Id and Name.
I expect to have a table Movies_Persons with columns Movie_Id and Actor_Id
How can I achieve this?
Your Problem is, that you don`t tell the Person Class, that there can be multiple Movies per person.
So by adding the following line in your person class:
public virtual ICollection<Movie> Movies { get; set; }
Your entity knows that both your classes can have multiple references to the other class.
To fulfill this requirement Entity Framework will create a third table with Movie_ID and Person_ID.
If you want more informations just look for:
Entity Framework - Many to many relationship
or follow this link:
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
You can check out the other articels on that page too, if you are new to entity framework.
UPDATE:
Sorry i missed, that you are already have another reference to your person table.
Here you have to tell your entity framework, which way you want to reference the two tables by fluent api.
Check out this stackoverflow answer. That should do the trick.
You have to insert this code into your OnModelCreating Function of your DbContext Class.
So your final code should look like this:
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
public virtual Person Director { get; set; }
public virtual ICollection<Person> Actors { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Movie> Movies_Actors { get; set; }
public virtual ICollection<Movie> Movies_Directors { get; set; }
}
And in your OnModelCreating add following code:
modelBuilder.Entity<Movie>()
.HasMany(a => a.Actors)
.WithMany(a => a.Movies_Actors)
.Map(x =>
{
x.MapLeftKey("Movie_ID");
x.MapRightKey("Person_ID");
x.ToTable("Movie_Actor");
});
modelBuilder.Entity<Movie>()
.HasRequired<Person>(s => s.Director)
.WithMany(s => s.Movies_Directors);
I don't have the possibility to test the code, but that should do the trick.
If you have to do some adjustments to make it work, plz add them in the comments, so other ppl can benefit from it.
I have two tables in my student's name and course name. There is a lot of relationship between them. How do I draw the lessons of the learner? Below are the entities I wrote.
public class Student
{
public Student()
{
Lessons = new HashSet<Lesson>();
}
public int ID { get; set; }
public int MaxCredit { get; set; }
public int MemberID { get; set; }
public virtual Member Member { get;
public virtual ICollection<Lesson> Lessons { get; set; }
}
public class Lesson
{
public Lesson()
{
Students = new HashSet<Student>();
}
public int ID { get; set; }
public string Name { get; set; }
public int LessonCredit { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
I tried to get a students lessons with:
uow.LessonRepository.GetALL().Where(x=> x.Students.Any(s=> s.MemberID==id))
but it gave me a casting error.
Your EF model is correct, to do a many to many relationship both sides have a virtual ICollection<T> of the other type.
Your query isn't right though if you want to get a student's lessons, parsing it we start with a LessonRepository (presumably an IEnumerable<Lesson>) and then do a Where on it to filter the collection down; so far so good (a bit odd starting with the lessons, but whatever). The condition is (in English) where there are any students with a MemberId matching "id" (wherever that came from). Not exactly sure what that would give you, but its not a student's lessons.
Since you already have the navigation property, you can just access the Lessons property of a given student object. Assuming you started with a student id something like:
Students.FirstOrDefault(s => s.Id == id)?.Lessons;
FirstOrDefault searches for the correct student item and then you just access the Lessons property.
I have a very simple question. I am new to ASP.NET MVC and very much confused about relationships while following code-first technique.
I have two model classes. I want to describe it as one person can have many courses.
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonId { get; set; }
}
public class Course
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CourseId { get; set; }
// public virtual ICollection<Course> Courses{ get; set; }
/* I removed above line from model because it was not creating any Course Field in Db table of Person and added a third table */
}
In order to make a relationship I created another model class that contains Id of persons and repeating Id's of the course
public class ModelJoin
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ModelJoinId { get; set; }
[ForeignKey("Person")]
public int PersonId { get; set; }
public virtual Person Person{ get; set; }
//One to many relationships
public virtual ICollection<Course> Courses{ get; set; }
}
So model join will have only two properties. I want to ask how we achieve this in a best way.
Value of courses will always be null so we can not add any course in it. Where in the code we will assign it a object?
There are a lot of questions on stackoverflow but no one describes it from scratch.
Is there any tutorial for add update delete tables with foreign keys.
Any help would be appreciated.
I think you need many to many relationship as one student can be enrolled for many courses and one course can be taken by many students.
Look at this:
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
The course collection should be in the Person class, try this :
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonId { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CourseId { get; set; }
}
So for example if you want to add a person with a course.
var person = new Person();
var courses = new List<Course>();
courses.Add(new Course());
person.Courses = courses;
dbContext.Persons.Add(person);
dbContext.SaveChanges();
My stack is ASP.NET MVC 5, Entity Framework 6.1, code-first, SQL Server.
I'm working on an application which involves multiple schools, each of which have courses (each which have sections) and students. These form hierarchies of related objects, which are each rooted by a single school instance.
Basic layout so far:
One school has many courses and students
One course has many sections
Simplified versions of the models follow.
public class School
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class Student
{
public int Id { get; set; }
public int SchoolId { get; set; }
public virtual School School { get; set; }
public virtual ICollection<Enrolment> Enrolments { get; set; }
}
public class Course
{
public int Id { get; set; }
public string Title { get; set; }
public int SchoolId { get; set; }
public virtual School School { get; set; }
public virtual ICollection<CourseSection> CourseSections { get; set; }
}
public class CourseSection
{
public int Id { get; set; }
public int CourseId { get; set; }
public virtual Course Course { get; set; }
public virtual ICollection<Enrolment> Enrolments { get; set; }
}
There are other models and relationships involved, but this should be enough to form the basis for my question.
A course section is related to a course, which in turn relates to a school. Given a course section, I can determine the school it belongs to, e.g. var school = givenSection.Course.School. Conversely, given a school, I can get the course sections belonging to the school. In code it's just a couple of references away, while in the database it's just a couple of table joins. But it gets more interesting still. Consider the next model:
public class Enrolment
{
public int Id { get; set; }
public int StudentId { get; set; }
public int CourseSectionId { get; set; }
public virtual Student Student { get; set; }
public virtual CourseSection CourseSection { get; set; }
}
An Enrolment instance is the many-to-many bridge between students and course sections. Getting a list of enrolments for a school is multiple steps requiring multiple table joins. In a system which might become quite large in number of records, I worry about the efficiency of this setup. Then again, the application is configured for lazy loading, so maybe that's OK, I don't yet know enough about EF to be certain.
In the interest of simplicity for data retrieval, might it be ideal to reference the school from either of, or both, the CourseSection and Enrolment models? Extending it further, should all models in the hierarchy be able to directly reference the School they belong to?
No, that would break normalization. Your performance concern is valid but the solution cannot be known and should not be implemented prematurely and without measuring actual timings. In my opinion the data is the most important thing because chances are it will outlive the code. Therefore all else equal the good shape of the data should be e priority.
I asked a question recently, and quite frankly, from the answer that was given, I am second guessing my entire strategy/how I design the classes and database.
I have not yet used the virtual keyword, nor Icollection at all in any of my Entity Framework projects, and quite frankly, after reading about it in some of the examples I do not fully understand why it is needed, or how it works.
In a sample application, I have a simple design where there are three lists - people, notes and pictures. The relationships are such that a person can own multiple notes and pictures, as well as people having a logo which is a picture.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
When I want to select a list of notes that a person owns, I simply perform db.Notes.Where(x=>x.owner=="y") on the notes object. I think I understand that if I were to use Icollection on the person class, I could instead perform something along the lines of db.person.select(x=> x.notes) to retrieve all the notes. Am I correct in this thinking?
If you were in my position with the relatively simple example above, how would you build the classes (involving ICollection, virtual or anything else)?
In addition and most importantly, the above is just an example, however in my actual application, I have used a very similar structure where I use my custom type as the "connector"/Foreign Key.
In many examples I have been reading, (in the above example) they would be using public int OwnerID instead of public person Owner. This has really thrown me and I am questioning my entire EF strategy. What are the differences?
Any advice would be greatly appreciated.
I think you are making this more difficult that is needed. If you were laying out regular classes you would relate them to each other rather than finding related id's and loading them separately which you are doing in your example.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public ICollection<Note> Notes { get; set; }
public ICollection<Picture> Pictures { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
So now say you have gotten your person object using the query
var person = _context.People.Where(m=>m.ID=randomIntWeWant).First();
We can get all related items as properties.
For Notes
person.Notes
For Photos
person.Photos
ICollection is related to lazy loading. By declaring a property as ICollection on one side, your are saying you have a many-to-one relationship between the objects. If you declare a property as ICollection on both sides, you are saying it is a many-to-many relationship. EF takes care of creating the tables that track that relationship.