Объекты EF против сервисных моделей и моделей просмотра (MVC)
Я пытаюсь понять и определить хорошие практики для разработки моделей вашего приложения / домена (POCOs/DTOs).
Допустим, у меня есть следующая таблица базы данных, Account:
UserID int
Email varchar(50)
PasswordHash varchar(250)
PasswordSalt varchar(250)
Конечно, EF4 будет строить сущность так:
public class Account
{
public int UserID { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
public string PasswordSalt { get; set; }
}
Теперь, допустим, у меня есть модель представления для регистрации нового пользователя, которая может выглядеть примерно так:
public class RegistrationViewModel
{
public string Email { get; set; }
public string Password { get; set; }
}
Наконец, у меня есть сервис, который должен зарегистрировать пользователя:
public class RegistrationService
{
public void RegisterUser(??? registration)
{
// Do stuff to register user
}
}
Я пытаюсь выяснить, что передать в метод RegisterUser. Модель представления, конечно, находится под моим веб-приложением (уровень представления), поэтому я не хочу, чтобы это передавалось моему сервису.
Итак, я думаю, одна из четырех возможностей:
1) Установите модель сервиса, которая похожа, если не идентична, на RegistrationViewModel, и используйте это:
public class RegistrationServiceModel
{
public string Email { get; set; }
public string Password { get; set; }
}
public class RegistrationService
{
public void RegisterUser(RegistrationServiceModel registration)
{
// Do stuff to register user
}
}
2) Настройте интерфейс модели и унаследуйте это в моей модели представления, и настройте мой метод для принятия интерфейса:
public interface IRegistrationModel
{
string Email;
string Password;
}
public class RegistrationServiceModel : IRegistrationModel
{
public string Email { get; set; }
public string Password { get; set; }
}
public class RegistrationService
{
public void RegisterUser(IRegistrationModel registration)
{
// Do stuff to register user
}
}
3) Передайте сущность Account, выполнив сопоставление RegistrationViewModel-to-Account в моем контроллере:
public class RegistrationService
{
public void RegisterUser(Account account)
{
// Do stuff to register user
}
}
4) Переместите мою модель представления из презентации в слой домена / службы и передайте ее в метод службы:
public class RegistrationService
{
public void RegisterUser(RegistrationViewModel account)
{
// Do stuff to register user
}
}
Ни один из этих трех сценариев не кажется идеальным, поскольку я вижу проблемы в каждом из них. Поэтому мне интересно, есть ли другой метод, о котором я не могу думать.
Каковы хорошие практики для этого?
Заранее спасибо.
3 ответа
Вы никогда не передадите модель представления в сервис. Служба даже не знает о существовании модели представления, которую вы могли определить на уровне представления. Сервис работает с моделями доменов.
Используйте Auto mapper для сопоставления между моделью представления и моделью домена и наоборот.
Лично я никогда не слышал о сервисных моделях в DDD (просмотр моделей для сервисов).
Используйте третий вариант, наверняка. Как сказал Шлякер, Сервис должен не знать о презентационной части приложения (частью которой является ваша ViewModel).
Конечно, также не переусердствуйте, включив множество моделей переходов, таких как RegistrationServiceModel
или - еще хуже - IRegistrationModel
(последний приведет к "взрыву интерфейса" однажды).
Так:
- Имейте сущность Домена (сущность POCO, которая сохраняется с Entity Framework или NHibernate или NoRM или любым другим).
- Имейте ViewModel, который представляет вашу модель домена в данном контексте. Не стесняйтесь сделать
ViewModel
за действие контроллера при необходимости. Побочным эффектом строгих ViewModels (те, которые имеют 1:1 с вашим View) является полное отсутствие проблем чрезмерной публикации и недостаточной публикации. Это зависит от вашей конкретной ситуации / вкуса. - Используйте атрибуты DataAnnotation с вашими ViewModels, чтобы обеспечить базовую проверку (не забывайте проверять и бизнес-правила, но они должны находиться за сетью - внутри слоя Services/Repositories).
- Никогда не сообщайте Службе приложений о ViewModels. Создайте экземпляр объекта домена и передайте его в службу (для проверки / сохранения).
- Используйте AutoMapper в качестве опции для быстрого сопоставления объектов вашего домена с ViewModels.
- Карта из входящих
ViewModel
или жеFormCollection
к вашей сущности либо в действии контроллера, либо вIModelBinder
, - (Необязательно) Я бы порекомендовал следовать принципу грома. Это действительно очень удобное использование ViewModels.
В этом случае имеет смысл использовать DTO (Data Transfer Object). Вы можете создать AccountDto
Класс на уровне сервиса и использовать его для передачи регистрационных данных в сервис. В некоторых случаях он может быть похож на ViewModel, но, как правило, вы можете отображать в своем представлении гораздо больше, чем требуется для создания пользователя. Чтобы дополнительно проиллюстрировать это, ваша ViewModel, вероятно, будет выглядеть примерно так:
public class RegistrationViewModel
{
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
[Required]
[Compare("Password")]
public string RepeatPassword { get; set; }
}
В то время как ваш DTO потребует только Email
а также Password
свойства.
public class AccountDto
{
public string Email { get; set; }
public string Password { get; set; }
}
Итак, как вы видите, ViewModel
содержит только данные, необходимые для View
, Логика проверки электронной почты и сравнения паролей происходит на вашем веб-уровне. Вы используете DTO, чтобы получить только электронную почту и пароль к Сервису. Затем на служебном уровне вы хэшируете пароль, заполняете объект Entity и сохраняете значения в базе данных.