Архитектура ASP.NET в MVC: модель представления по составу, наследования или дублирования?
я использую ASP.NET сначала код MVC 3 и Entity Framework 4.1.
Допустим у меня есть User
сущности :
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
при редактировании его в мой UserController
Я хочу добавить PasswordConfirmation
поле и убедитесь в том, что PasswordConfirmation == Password
1. По составу
моя первая попытка была :
public class EditUserModel
{
[Required]
public User User { get; set; }
[Compare("User.Password", ErrorMessage = "Passwords don't match.")]
public string PasswordConfirmation { get; set; }
}
в этом случае проверка на стороне клиента работает, а (Edit: работа проверки на стороне клиента была совпадением.) не работает и сбой проверки на стороне сервера появляется следующее сообщение : не удалось найти свойство с именем User.Пароль
Edit: я думаю, что лучшим решением в этом случае было бы создать пользовательский CompareAttribute
реализация IValidatableObject
public class EditUserModel : IValidatableObject
{
[Required]
public User User { get; set; }
public string PasswordConfirmation { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(this.PasswordConfirmation != this.User.Password)
return new[] { new ValidationResult("Passwords don't match", new[] { "PasswordConfirmation " }) };
return new ValidationResult[0];
}
}
в этом случае проверка на стороне сервера работает но проверка на стороне клиента не работает больше. Реализация IClientValidatable
кажется немного слишком сложным, и я предпочитаю не иметь проверки на стороне клиента в этом случае.
2. По наследству
public class EditUserModel : User
{
[Compare("Password", ErrorMessage = "Passwords don't match.")]
public string PasswordConfirmation { get; set; }
}
при попытке непосредственно сохранить EditUserModel
используя EF это не работает, я получаю некоторое сообщение об ошибке EditUserModel
метаданные, поэтому я использую AutoMapper для преобразования из User
до EditUserModel
и в обратном направлении.
Это решение работает но это сложнее, потому что я придется преобразовать из модели в модель представления и обратно.
3. Путем дублирования
(предложил Мальте Класен)
вид модели будет иметь все свойства модели плюс дополнительные. AutoMapper может быть использован для преобразования из одного в другой.
public class EditUserModel {
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[Compare("Password", ErrorMessage = "Passwords don't match.")]
public string ConfirmPassword { get; set; }
}
это решение мне нравится меньше всего из-за дублирования кода (сухой)
вопросы
каковы плюсы и минусы наследования, композиции и дублирования в этом случае ?
есть простой способ как на стороне клиента и проверка на стороне сервера без необходимости преобразования модели в модель представления и обратно ?
5 ответов:
борясь с этим вопросом Раньше, я в разных случаях пошел со всеми тремя. В общем, большинство мнений, которые я видел, поддерживают дублирование в проекте MVC, с ViewModel, построенным специально для каждого представления. Таким образом, конвенция, которую вы используете, - это что-то вроде
UserDetailsViewModel
иUserCreateViewModel
. Как вы сказали, в этот момент AutoMapper или какой-либо другой инструмент автоматического отображения будет использоваться для преобразования объектов вашего домена в эти плоские модели просмотра.хотя я тоже, не нравится повторять код, мне также не нравится загрязнять объекты моего домена проверкой или другими атрибутами, специфичными для представления. Еще одно преимущество, хотя, по общему признанию, с которым почти никому никогда не придется бороться (независимо от того, что говорят все профессионалы), заключается в том, что вы можете манипулировать объектами своего домена некоторым образом, не обязательно манипулируя своими моделями просмотра. Я упоминаю об этом, потому что это обычно цитируется, а не потому, что это имеет большой вес для меня.
наконец, используя действительно плоский ViewModel делает для более чистой разметки. Когда я использовал композицию, Я часто делал ошибки, создавая HTML-элементы с именами, которые являются чем-то вроде
User.Address.Street
. Плоская модель просмотра уменьшает, по крайней мере, мою вероятность этого (я знаю, что всегда могу использовать процедуры HtmlHelper для создания элементов, но это не всегда возможно).мои последние проекты также в значительной степени требовали отдельных ViewModels в эти дни в любом случае. Все они были основаны на NHibernate, и использование прокси на NHibernate объекты не позволяют использовать их непосредственно для представлений.
обновление - вот хорошая статья, на которую я ссылался в прошлом: http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx
вы также можете рассмотреть независимые классы для моделей домена и представления, в этом случае например
public class EditUserModel { public string Name { get; set; } public string Email { get; set; } public string Password { get; set; } public string ConfirmPassword { get; set; } }
если идентификатор хранится в url. Если вы хотите избежать копирования вручную между экземплярами User и EditorUserModel,AutoMapper могу помочь вам. Таким образом, вы можете легко отделить строку пароля в модели представления от хэша пароля в модели домена.
Я пытаюсь разобраться в этом, и я нашел решение, которое не включает в себя дублирование кода. Это своего рода обходной путь, но, на мой взгляд, это лучше, чем другие предлагаемые решения.
У вас есть модель пользователя со всеми проверки:
public class UserModel { [Required] public int Id { get; set; } [Required] public string Name { get; set; } public string Email { get; set; } public string Password { get; set; } }
вы составляете предыдущую модель с новой моделью
public class EditUserModel { public UserModel User { get; set; } [Required] public string PasswordConfirmation { get; set; } }
трюк в действии, вы можете получить более одной модели:
[HtttPost] public ActionResult UpdateInformation(UserModel user, EditUserModel editUserModel) { if (ModelState.IsValid) { // copy the inner model to the outer model, workaround here: editUserModel.User = user // do whatever you want with editUserModel, it has all the needed information } }
таким образом, проверка работает ожидаемый.
надеюсь, что это помогает.
Я не использую модели сущностей слишком много, я предпочитаю модели LINQ-SQL, поэтому это может быть неверно:
почему бы не использовать класс метаданных, который применяется к сущности? С LINQ-SQL назначенные метаданные учитываются как на стороне клиента, так и на стороне сервера.
из того, что я понимаю, применение атрибута [MetaDataType] похоже на наследование, только оно работает без реализации нового класса (модели) для изменений в базовом сущность.
кроме того, другой вариант, который вы можете попробовать, - это создание пользовательского атрибута-я сделал это один раз для аналогичной цели. По существу флаг, который указывает на сохранение члена.
поэтому я бы определил сущность следующим образом:
public class User { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Password { get; set; } [DoNotPersist] public string ConfirmPassword {get; set;} }
кроме того, я не знаю, что вы делаете для хранения данных, но я подключил переопределение в функции OnInserting, OnEditing, OnDeleting для моего DataContext, которые в основном удаляли любые члены, имеющие мой настраиваемый атрибут.
Мне нравится этот метод простой, потому что мы используем много временных, а не алгоритмических данных для каждой модели (создание хорошего пользовательского интерфейса для бизнес - аналитики), который не сохраняется в базе данных, но используется везде внутри функций модели, контроллеров и т. д.-Поэтому мы используем инъекцию зависимостей во всех репозиториях моделей и контроллерах, и поэтому у нас есть все эти дополнительные точки данных для каждой таблицы.
надеюсь, что это поможет!
PS: - композиция vs наследование-это действительно зависит от целевого пользователя приложения. Если это для приложения интрасети, где безопасность меньше проблем, а среда пользователя / браузера контролируется, то просто используйте проверку на стороне клиента, т. е.: состав.
Я бы предпочел композицию наследованию.
в случае вашего пароля пользователя похоже, что вы на самом деле храните пароль в таблице пользователей в открытом тексте, что очень и очень плохо.
вы должны хранить только соленый хэш, и ваш
EditUserModel
должны иметь два свойства строки для пароля и подтверждения пароля, которые не являются полями в таблице.