Entity Framework: Object design with relationships and more

92 views Asked by At

I have a .net solution (with Entity Framework, using the CodeFirst approach) about the administration of a university. The architecture of the project is like this:

  • DataAccess (DbContext and Generic Repository)
  • Entities (code first classes)
  • Services (services classes)
  • Web (an MVC project, not important for the explanation)

For making the question easier, i'll only talk about two classes from my domain (on the Entities layer):

  • Course
  • Student

The course can have many students. And the objective of the solution, is to add students to the courses (with some validation in the middle).

Course.cs

public class Course
{
    [Key]
    public int IdCourse { get; protected set; }

    public string Name { get; set; }

    public virtual ICollection<Student> Students { get; protected set; }

    public void AddStudent(Student student)
    {
        // Some Validation
        Students.Add(student);
    }
}

As you can see, the class has a property for adding students to the course. The situation is that the validation is growing and now is very complex, and it needs to make some queries to the database and more... So the course have a lot of responsibilities!

So i thought, that i needed a service for handling the "Adding" and making all the validation and take this reponsibility away from the Course class. The problem is that, as the Students Collection is a private member from Course, i can't add students to courses from the Service class.

Another way i thought to solve the problem, is adding the related entity (the student) to the context (from the service), but it would break my repository (because i would have to make the DbContext public). And if i use the DbContext from the Service class directly, the repository has no point, hasn't it?

So, how should i design that? or what should i do to solve the problem? Any suggestion would be very well received too!

Thanks!

2

There are 2 answers

1
Dave Nichol On

This doesn't directly answer your question, especially as related to code-first EF and all that (not my area of expertise). But it does look like you have a design issue here.

I'm a big fan of DDD, or Domain-Driven Design. Some questions you might want to ask include:

  • Should "AddStudents" really be a method? An object should have a behavior, while "adding students" is a data-persistence concept, not a domain/entity concept. Perhaps the behavior would be "enrolling"? Enrolling may have its own logic, and thus objects to encapsulate that logic.
  • Second, do students exist outside the concept of a course they're taking? E.g., can they take multiple courses? Then the collection maybe shouldn't be private here...should there be a new object called StudentEnrollment?

There are others, but definitely take a holistic look at your problem and the way your objects are designed. Is adding a student the course the same as adding the course to that student?

Maybe your service could do the validation and either add the student to the course, or the course to the student (or both) and persist them in their own repositories.

In any event, I'd recommend Julie Lerman's series on DDD for data-oriented EF people: http://msdn.microsoft.com/en-us/magazine/dn342868.aspx.

In any event, I would recommend not making your DbContext public!

3
IAbstract On

I don't agree with a method like AddStudent(...) in the entity. Let entities be simple data transfer objects - that is, POCOs - into and out of the EntityFramework. I prefer to let my business logic take care of such things.

You can set the IdCourse and Students setter to private. I have seen others use protected as well ... but why? Are there descendants of an entity?

One solution for ICollection<T> properties recommended by the likes of Julie Lerman is:

private ICollection<Student> _students;  

public virtual ICollection<Student> Students { 
   get; { _students ?? (_students = new Collection<Student>()); }
   private set { _students = value; }
}  

Update (from comment)

If you use a repository or business layer to query for a Course, you could have a method as follows:

   // this is how I would approach a business layer method
   void AddStudentToCourse(string CourseName, Student NewStudent) {
       // may employ a using block for repository
       // also validate student
       using (var repo = new CourseRepository()) {
          var course = repo.GetCourse(CourseName);
          course.Students.Add(NewStudent);

          repo.Save();
       }
   }

There are a few different ways to accomplish this and this sample is far from complete but should provide you with a basic concept.

The underlying DbContext (either inherited or encapsulate by CourseRepository) will track changes to Course and the addition of a new student or relationship of the Student to the Course.

As far as the use of private or protected setters, I wanted to limit developer access to changing IDs/Keys or reassigning entire collection references.