Что вы делаете, когда у вас есть богатые модели?

#c# #design-patterns #mvvm #viewmodel #models

#c# #шаблоны проектирования #mvvm #viewmodel #Модели

Вопрос:

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


  • Расширенные модели имеют проверку данных. Есть простой способ повторно использовать их с помощью ViewModels?

Пример:

 public class PersonViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private Person _person;

    [Required] //This seems redundent...
    public String FirstName { ... }
}

public class Person
{
    [Required]
    public String FirstName { ... }
}
  

Это всего лишь один пример … в принципе, если у вас есть богатая модель, есть ли какой-нибудь способ воспользоваться этим при сохранении MVVM и избежать избыточного кода? Мне бы очень хотелось, чтобы мои модели не были каким-либо контекстом данных или полностью отображались ViewModel.

Например:

 public class PersonViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private Person _person;

    //This seems like a bad thing to do...
    public Person ThePerson { get { return _person; } }
}
  

Ответ №1:

Это что-то вроде классического вопроса.

Если вы «загрязняете» свои классы моделей интерфейсами и атрибутами, специфичными для уровня представления, вы получаете эффективность, имея только одну версию любой логической модели, но теряете способность презентации и бизнес-модели развиваться независимо.

Если вы сохраняете свою модель «чистой» и поддерживаете отдельную модель представления, вы получаете гибкость в каждой из них, но теряете эффективность из-за необходимости поддерживать (и сопоставлять) две версии.

С точки зрения теории и для более сложных систем я бы рекомендовал последнее. Если ваша система относительно проста (например, CRUD), и вы не ожидаете, что вам нужно, чтобы два типа моделей развивались независимо, вы, вероятно, в полной безопасности с первым.

Очевидно, что эти два метода не являются взаимоисключающими, и принятие решения на основе каждого экрана не является чем-то неслыханным.

Ответ №2:

Одна из идей MVVM — отделить уровень представления от уровня данных. Это дает вам возможность изменять данные, с которыми работает уровень представления, без изменения данных уровня данных.

Таким образом, данные из уровня представления записываются на ваш уровень данных только по запросу пользователя. Ваше избыточное свойство FirstName работает как граница слоя, что дает вам гибкость для реализации чего-то вроде простого «отменить все изменения».

Рассмотрите возможность использования универсального ValueViewModel, который обрабатывает уведомления об изменениях значений, поскольку они требуются для привязки данных. Следуя этому подходу, ваша модель представления будет выглядеть примерно так:

 public class PersonViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private Person _person;

    [Required] //This seems redundent...
    public ValueViewModel<String> FirstName { ... }
}
  

Используя этот шаблон, вашей модели не нужно реализовывать интерфейс INotifyPropertyChanged, который снова отделяет ваш уровень представления от вашего уровня данных.

MVVM — это очень сложный шаблон, и вы часто будете видеть вещи, которые на первый взгляд кажутся немного формальными, но следование шаблону дает вам большую гибкость. Если вы решите нарушить правила MVVM, вы поставите под угрозу всю архитектуру вашего приложения, потому что одно это нарушение разрушает гибкость, которую вы получили с помощью mvvm. Таким образом, если вы планируете нарушать MVVM, подумайте о том, чтобы вообще не использовать его.

Ответ №3:

Я никогда не видел ничего плохого в том, чтобы предоставлять всю модель из ViewModel для представления. Я знаю, что это не «MVVM-пуристский» подход, но он прост, быстр и хорошо работает.

Но я понимаю, что оба метода одинаково действительны, и обычно для очень большой кодовой базы разделение между Model и ViewModel облегчает жизнь в долгосрочной перспективе. В таком случае, почему бы вашей проверке ViewModel не вернуть вашу ModelValidation? Это не так красиво, как использование примечаний к данным, но вы не будете создавать свою проверку в нескольких местах.

Например, я часто использую что-то вроде этого:

 public string GetValidationError(string propertyName)
{
    string s = null;

    switch (propertyName)
    {
        case "FirstName":
        case "LastName":
            s = Person.GetValidationError(propertyName);
            break;
    }

    return s;
}

string IDataErrorInfo.this[string propertyName]
{
    get { return this.GetValidationError(propertyName); }
}