Является ли хорошей практикой использование EntityObjects в качестве модели (MVC)?
Я создаю свое первое веб-приложение MVC 4/Razor, используя Entity Framework 5, и делаю небольшую домашнюю работу, прежде чем принимать какие-либо дизайнерские решения.
Я вижу, что объекты EF происходят от EntityObject
, который, похоже, содержит множество полезных примеров лучших практик, не в последнюю очередь это оптимистичная обработка параллелизма. Другими словами, если 2 человека загружают запись Джейн Доу с 123 Maple Street одновременно, первая меняет свое имя на Jane Smith, а вторая меняет свой адрес на 321 Maple Street, тогда очень легко разрешить объединение обоих изменений в запись без конфликтов, в то время как попытка второго пользователя изменить то же поле, что и у первого пользователя, приведет к ошибке.
С другой стороны, кажется довольно стандартной практикой создание облегченных объектов передачи данных для передачи данных между сервером и клиентом, которые служат или являются моделями для инфраструктуры MVC. Это отлично подходит для обеспечения минимального трафика для клиента, но затягивает проверку параллелизма.
Поэтому я подвергаю сомнению обоснованность использования DTO. Каковы причины использования DTO? Насколько плохо использовать EntityObject
как или в модели MVC? Какое другое решение вы бы предложили, чтобы включить оптимистическую обработку параллелизма, как я описал выше?
5 ответов
Я вижу только плохие моменты при прохождении EntityObject
непосредственно к виду:
- Вы должны сделать ручной или черный список вручную, чтобы предотвратить чрезмерную публикацию и массовое назначение
- Случайно лениво загружать дополнительные данные из вашего представления становится очень легко, что приводит к выбору проблем N+1
- По моему личному мнению, модель должна точно повторять информацию, отображаемую в представлении, и в большинстве случаев (кроме базовых элементов CRUD) представление содержит информацию из более чем одного
EntityObject
= Публикация комментария как ответа =
Объекты EF являются POCO начиная с пары версий назад (не знаю, какие именно). Если вам нужен "EntityObject", вы должны использовать какой-то адаптер (я полагаю, что есть один для облегчения миграции приложений, но я бы не рекомендовал использовать его как часть нового проекта).
Если в ваших модельных классах есть методы, связанные с EF, то да, это действительно плохо. Но EF 5 не должен. Я полагаю, что начиная с 4.1 они используют Proxies вместо того, чтобы расширять EntityObject именно по этой причине - чтобы было удобно использовать их в качестве Моделей.
Просто посмотрите на ваши.tt и сгенерированные.cs файлы. Они простые POCO. Нет интерфейсов, нет базовых классов. Если вы получаете объект из структуры сущностей и проверяете тип объекта, вы найдете что-то вроде System.Data.Entity.DynamicProxies.Employee_5E43C6C196[...]
, Это класс, сгенерированный прокси. Однако, если вы делаете то же самое, но изменяете конфигурацию контекста базы данных раньше (dbContext.Configuration.ProxyCreationEnabled = false;
), вы заработали себе хорошую сущность Сотрудника!
Таким образом, чтобы ответить на исходный вопрос, вполне приемлемо / хорошая практика использовать EF POCO в качестве моделей, но убедитесь, что вы используете их как непостоянные объекты.
Дополнительная информация
Вы должны рассмотреть концепции DDD и реализацию соответствующих DDD паттернов, таких как репозитории или что-либо, что вы считаете удобным.
Вы никогда не должны использовать эти объекты непосредственно в представлениях, постоянных или непостоянных.
Вы должны прочитать об AutoMapper, чтобы сделать вашу жизнь проще (хорошо сочетается с репозиториями или автономно). Это облегчит передачу из ProxyEmployee -> Employee -> ViewModel и наоборот.
Пример ужасного использования EF-сущностей:
return View(dbContext.employees.First());
Пример неправильного использования объектов EF № 1:
Employee e = dbContext.employees.First();
return View(new Employee { name = e.name, [...] });
Пример неправильного использования #2 сущностей EF:
Employee e = dbContext.employees.First();
return View(new EmployeeViewModel{ employee = e });
Пример правильного использования EF-сущностей:
Employee dbEmploye = dbContext.employees.First();
Employee e = new Employee { name = dbEmploye.name, [...] };
return View(new EmployeeViewModel { employee = e });
Пример правильного использования объектов EF:
Employee e = dbContext.employees.First();
EmployeeViewModel evm = Mapper.Map<Employee, EmployeeViewModel>(e);
return View(evm);
Пример потрясающего использования EF-сущностей:
Employee e = employeRepository.GetFirstEmployee();
EmployeeViewModel evm = Mapper.Map<Employee, EmployeeViewModel>(e);
return View(evm);
Как Чак Норрис сделал бы это:
return View(EmployeeViewModel.Build(employeRepository.GetFirstEmployee()));
On the other hand, it seems pretty standard practice to create lightweight Data Transfer Objects to pass data between the server and the client, and which serve as or in models for the MVC framework. This is great for ensuring minimal traffic to the client, but it screws up concurrency checking
Здесь вы, кажется, говорите о том, что течет по проводам в браузер. В этом смысле, даже если вы используете классы EntityObject в вашем контроллере, данные все равно должны быть представлены клиенту и отправлены обратно в более простой форме. Так что любая поддержка параллелизма не очень актуальна, когда вы находитесь на клиенте.
У Dtos есть несколько преимуществ, но это зависит от того, что вы планируете.
Если вы спроектируете их как плоские, то они будут лучше и проще для следующего:
- отображение для просмотра моделей
- кэширование этих объектов dto, так как ваши доменные объекты могут иметь большой граф и не подходят для помещения в кеш. Dtos может дать вам хорошую детализацию для того, что и как он кэшируется
- упрощение подписей API и предотвращение неожиданной поздней загрузки прокси-объектов
- отправив данные по проводам, прочтите о стриппере
Но если вам не нужно ничего из этого, то вы можете просто обойтись без.
На основании того, что все ответы до сих пор указывают, что:
- это плохая практика
EntityObject
клиенту, основная причина в том, что вы рискуете случайно лениво загрузить несметное количество связанных данных - DTO портят вашу способность выполнять оптимистическую проверку параллелизма
Я предложу свое собственное решение и предложу свои комментарии о том, считаете ли вы, что это хорошая идея.
Вместо того, чтобы передавать простой ванильный DTO клиенту, мы создаем реферат GenericDTO<T>
класс, где T
представляет собой EntityObject
, Мы также создаем GenericDTOProperty<T>
класс, который имеет два свойства:
public class GenericDTOProperty<T> {
public T LoadValue { get; internal set; }
public T Value { get; set; }
}
Теперь, скажем, у меня есть EntityObject
называется "Клиент", со свойствами "ID" и "Имя". Я хотел бы создать класс DTO, как это:
public class CustomerDTO : GenericDTO<Customer> {
public GenericDTOProperty<int> ID;
public GenericDTOProperty<string> Name;
}
Не вдаваясь в слишком много умозрительного кода, я бы инкапсулировал код в GenericDTO
класс, который использует отражение для копирования значений в и из EntityObject
, установив LoadValue
при загрузке и проверке, что он не изменился при сохранении. Вы можете стать еще умнее и поставить ID
свойство в базовом классе, если вы уверены, что каждая таблица будет иметь одно поле идентификатора.
Это похоже на разумную модель? Это кажется достаточно простым - почти слишком простым... что заставляет меня беспокоиться, что, возможно, что-то не так с этим, в противном случае это должно быть практически частью структуры?