Entity Framework: объектный дизайн с отношениями и многое другое

У меня есть решение.net (с Entity Framework, использующим подход CodeFirst) об администрировании университета. Архитектура проекта выглядит следующим образом:

  • DataAccess (DbContext и универсальный репозиторий)
  • Сущности (код первых классов)
  • Услуги (классы услуг)
  • Веб (проект MVC, не важен для объяснения)

Для упрощения вопроса я расскажу только о двух классах из моего домена (на уровне сущностей):

  • Курс
  • Ученик

Курс может иметь много студентов. И целью решения является добавление студентов на курсы (с некоторой проверкой в ​​середине).

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);
    }
}

Как видите, у класса есть свойство для добавления студентов в курс. Ситуация такова, что валидация растет и сейчас очень сложна, и ей нужно сделать несколько запросов к базе данных и многое другое... Так что у курса много обязанностей!

Поэтому я подумал, что мне нужен сервис для обработки "Добавление" и выполнения всей проверки и удаления этой ответственности из класса курса. Проблема в том, что, поскольку коллекция студентов является частным участником курса, я не могу добавлять студентов в курсы класса обслуживания.

Еще один способ, который я решил решить эту проблему, - это добавить связанную сущность (студента) в контекст (из службы), но это сломало бы мой репозиторий (потому что мне пришлось бы сделать DbContext общедоступным). И если я использую DbContext из класса Service напрямую, репозиторий не имеет смысла, не так ли?

Так, как я должен проектировать это? или что я должен сделать, чтобы решить проблему? Любое предложение тоже будет очень хорошо принято!

Спасибо!

2 ответа

Это не дает прямого ответа на ваш вопрос, особенно в отношении кода EF и всего этого (не в моей области знаний). Но похоже, что у вас есть проблема дизайна здесь.

Я большой поклонник DDD или доменно-управляемого дизайна. Некоторые вопросы, которые вы можете задать, включают в себя:

  • Должен ли "AddStudents" действительно быть методом? У объекта должно быть поведение, в то время как "добавление учеников" является концепцией сохранения данных, а не концепцией домена / сущности. Возможно, поведение будет "зачислением"? Регистрация может иметь свою собственную логику и, следовательно, объекты для инкапсуляции этой логики.
  • Во-вторых, существуют ли студенты вне концепции курса, который они посещают? Например, они могут пройти несколько курсов? Тогда, возможно, коллекция не должна быть приватной... должен ли быть новый объект с именем StudentEnrollment?

Есть и другие, но определенно взгляните на вашу проблему и то, как спроектированы ваши объекты. Является ли добавление студента курсом таким же, как добавление курса к этому студенту?

Возможно, ваша служба могла бы выполнить валидацию и либо добавить студента в курс, либо курс в студента (или оба) и сохранить их в своих собственных репозиториях.

В любом случае, я бы порекомендовал серию Джули Лерман по DDD для ориентированных на данные людей EF: http://msdn.microsoft.com/en-us/magazine/dn342868.aspx.

В любом случае, я бы порекомендовал не делать ваш DbContext публичным!

Я не согласен с таким методом, как AddStudent(...) в сущности. Пусть сущности будут простыми объектами передачи данных - то есть POCO - в EntityFramework и из него. Я предпочитаю, чтобы моя бизнес-логика заботилась о таких вещах.

Вы можете установить IdCourse а также Students сеттер в приват. Я видел, как другие используют protected как хорошо... но почему? Есть ли потомки сущности?

Одно решение для ICollection<T> свойства, рекомендуемые подобными Джули Лерман, являются:

private ICollection<Student> _students;  

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

Обновление (из комментария)

Если вы используете репозиторий или бизнес-уровень для запроса курса, у вас может быть следующий метод:

   // 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();
       }
   }

Есть несколько различных способов сделать это, и этот пример далеко не полный, но он должен дать вам базовую концепцию.

Основной DbContext (наследуется или инкапсулируется CourseRepository) будет отслеживать изменения в Course и добавление нового студента или отношения Student к Course,

Что касается использования частных или защищенных сеттеров, я хотел ограничить доступ разработчика к изменению идентификаторов / ключей или переназначению ссылок на всю коллекцию.

Другие вопросы по тегам