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.
Related
I am new to ASP.NET MVC. Using Entity Framework 6, I am working on a project to store employee skills in a database. The user can enter a new skill into a list of skills. I would like to keep track of who added the new skill. I have a table of all of the employees.
These are the models for the two tables.
public partial class Skill
{
public int ID { get; set; }
public string Skill { get; set; }
[ScaffoldColumn(false)]
public int LastActionUserID { get; set; }
public virtual Employee Employees { get; set; }
}
public partial class Employee
{
public int ID { get; set; }
public string EmployeeLAN { get; set; }
public int LastActionUserID { get; set; }
public virtual Employee Employees { get; set; }//References itself for LastActionUserID
public virtual ICollection<Skill> Skills{ get; set; } //Omitted in initial question
}
There is a 1 to Many mapping of Employee to Skill. I can get the current user's EmployeeLAN but how do I get the id of that Employee record to put into the Skill table automatically when then new skill is created? Must I convert the table to an enumerable object and use SingleOrDefault or LINQ? Or is there an easier way using EF6? Also, setting this automatically when a new skill is created would be done in the controller, correct?
You're on the right track and you should continue to use EF6.
The Employee class should have a Skills list. That way you can call myEmployee.Skills and have a list of all the skills available.
public partial class Employee
{
public int ID { get; set; }
public string EmployeeLAN { get; set; }
public int LastActionUserID { get; set; }
public virtual ICollection<Skill> Skills{ get; set; }
}
Also, setting this automatically when a new skill is created would be done in the controller, correct?
You'll need to add to the Skills list, call AddOrUpdate() to mark this as changed, then SaveChanges() to persist it to the database.
I recommend learning more from the MSDN docs and Julie Learman's
Entity Framework videos on Pluralsight
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.
Well it's a noob question but i can't figure out the solution.
I have two entities, user and team. User can create a team and invite other users. User can belong only one team but team has multiple users.
What would be the most correct relationship between users and team? I also need DateJoin in team and other properties. Should i create third table(TeamMembers)?
Here is code what i tried :
public class TeamMember
{
public int Id { get; set; }
[ForeignKey("Team")]
public int TeamId { get; set; }
public Team Team { get; set; }
[ForeignKey("User")]
public string UserId { get; set; }
public User User { get; set; }
public DateTime DateJoin { get; set; }
public RoleEnum MemberRole { get; set; }
}
public enum RoleEnum
{
Capitan = 1,
Main,
Sub
}
And team has list of members:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public int Score { get; set; }
public ICollection <TeamMember> Members { get; set; }
}
But third table means many to many relationship...
If the user can only belong to one team then this is the correct format.
However, if you think you might change this policy in the future then I do suggest you use a many to many relationship. This would also allow you to keep track of the previous teams your users belonged to, by simply using a "DateLeave" property for example.
I am trying to use CodeFirst to generate my database.
I have two tables Staff and Team, There is one Team Leader in each team that is a foreign key to a staffID, and each staff is associated to one Team.
public class Staff
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public string Salt { get; set; }
public string Token { get; set; }
public string Mobile { get; set; }
public string Email { get; set; }
public bool Admin { get; set; }
public bool Active { get; set; }
public int TeamID { get; set; }
[ForeignKey("TeamID")]
public Team Team { get; set; }
}
public class Team
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int TeamLeaderID { get; set; }
[ForeignKey("TeamLeaderID")]
public Staff TeamLeader { get; set; }
}
Because each one is pointing to the other I get an error Unable to determine the principal end of an association between the types 'Team' and 'Staff'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations. How do I annotate it in such a way that it understands why I am doing this.
Think about it this way... what comes first the team or the team leader? If you try to create a team leader you can't because you have to first specify a team! But if you want to create a team you can't because you must specify who the team leader is according to your foreign key constraints.
You will have to ease up in some way and either make it so that a team can have an optional team leader, or a staff member can optionally belong to a team.
You do this by changing one of the foreign key IDs to a nullable type:
public int? TeamLeaderID { get; set; }
Your code seems like you are trying to include business rule enforcement/responsibility via referential integrity. You have a one to many relationship your Team -> Staff. You just add a boolean for the TeamLeader. Your logic before doing a database write should check to see if you have an existing TeamLeader or not already.
public class Staff
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public string Salt { get; set; }
public string Token { get; set; }
public string Mobile { get; set; }
public string Email { get; set; }
public bool Admin { get; set; }
public bool Active { get; set; }
public IsTeamLeader { get; set; }
}
public class Team
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
//virtual keyword tells Code First to create the proper Foreign Key relationship
public virtual List<Staff> Members{ get; set; }
}
If you had a large system, with many developers, you could use the fluent API to accomplish your goal and enforce your team leader rule at the database level, thus preventing an out of touch developer from inadvertently adding a second team leader to any give team but if this is a small to normal size project, with small teams that are aware of the basics of the company/project than a simple one to many relationship will accomplish the mission and you can rely on your business rules/logic to enforce/protect the database data so that there is one team leader for any given team at any give time. Consider an AddUpdateTeamMember type method that is called by everyone that enforces the team leader requirement. A stored procedure is another great way to easily solve this problem if you are still considering a model first approach and still experimenting with code first.
Consider too, what if there was a need for two team leaders on one team at some future point of time in the project, database might get too inconveniently "locked down" to scale to this change.
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.