Архитектура 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 53

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 должны иметь два свойства строки для пароля и подтверждения пароля, которые не являются полями в таблице.