MVVM с агрегированными модельными классами - как обернуть в ViewModels? - PullRequest
4 голосов
/ 31 августа 2010

В настоящее время я пытаюсь создать небольшое приложение, используя шаблон MVVM.Однако я не знаю, как правильно обернуть агрегированные классы Model в моей ViewModel.Из того, что я мало знаю о MVVM, вы не должны выставлять Модели в вашей ViewModel как свойства, иначе вы могли бы напрямую связываться с Моделью из вашего Представления.Поэтому мне кажется, что мне нужно обернуть вложенную модель в другую ViewModel, но это налагает некоторые проблемы при синхронизации Model и ViewModel позже.

Так как же вы это эффективно сделаете?

Я будуприведу короткий пример.Предположим, у меня есть следующие классы моделей:

public class Bar
{
    public string Name { get; set; }
}

public class Foo
{
    public Bar NestedBar { get; set; }
}

Теперь я создаю два класса ViewModel соответственно, оборачивая модели, но сталкиваюсь с проблемами с FooViewModel:

public class BarViewModel
{
    private Bar _bar;
    public string Name 
    { 
        get { return _bar.Name; }
        set { _bar.Name = value; }
    }
}

public class FooViewModel
{
    private Foo _foo;
    public BarViewModel Bar
    {
        get { return ???; }
        set { ??? = value; }
    }
}

Что теперь делатьЯ делаю с Bar-свойством FooViewModel?Чтобы "получить" работать, мне нужно вернуть экземпляр BarViewModel.Создать новое поле этого типа в FooViewModel и просто обернуть туда объект _foo.NestedBar?Изменения в свойствах этого поля должны распространяться вниз до базового экземпляра Bar, верно?

Что если мне нужно назначить другой экземпляр BarViewModel этому свойству, например, так:

foo.Bar = new BarViewModel();

Теперь, когда победилне распространяется на модель, которая по-прежнему содержит старый экземпляр типа Bar.Мне нужно создать новый объект Bar на основе новой BarViewModel и передать его _foo, но как вы это делаете элегантно?В этом примере это довольно тривиально, но если Bar намного сложнее с большим количеством свойств, это будет много печатать ... не говоря уже о том, что он будет очень подвержен ошибкам, если вы забудете установить один изсвойства.

Ответы [ 3 ]

7 голосов
/ 09 января 2011

@ Гоблин

В вашем коде есть некоторые недостатки: например, Что делать, если я получаю список объектов Foo из базы данных и хочу обернуть каждый из них в ObservableCollection?

тогда ваш конструктор FooViewModel должен принять модель Foo в качестве параметра, а не создавать ее внутри конструктора!

Обычно вы делаете это, чтобы обернуть модель в модель представления и поместить ее одновременно в привязываемую коллекцию:

IEnumerable<Foo> foos = fooRepository.GetFoos();
foos.Select( m => viewmodelCollection.Add(new ViewModel(m,e.g.Service)));

Свойства моделей не копируются в ад ViewModel нет !!! ViewModel делегирует свои свойства свойствам модели, например:

public class FooViewModel
{
   private Foo _foo;

   public FooViewModel(Foo foo,IService service)
   {
      _foo = foo;

   }

   public string FoosName
   { 
      get{return _foo.Name };
      set
      {
         if(_foo.Name == value)
            return;

         _foo.Name = value;
         this.NotifyPropertyChanged("FoosName");
      }
   }

}

И, как сказал Гоблин, все интерфейсы, специфичные для пользовательского интерфейса, такие как:

IDataErrorInfo
INotifyPropertyChanged
IEditableObject

и т.д ...

реализованы ТОЛЬКО в ViewModel.

3 голосов
/ 01 сентября 2010

Мой ответ выше имеет смысл, только если вы выполняете DDD - если нет - вы можете решить свою проблему следующим образом - просто «сплющив» модель:

public class FooViewModel
{
    private Foo _foo;
    public string Name
    {
        get { return _foo.Name; }
        set { _foo.Name = value; }
    }
    public string BarProperty
    {
        get { return _foo.Bar.Property; }
        set { _foo.Bar.Property = value; }
    }
}

Или вы могли бы поступить так, как я показал в предыдущем примере - просто игнорировать все, что касается агрегатов ... все равно должно работать.

1 голос
/ 01 сентября 2010

Хорошо - обо всем по порядку - использование термина Aggregate означает, что вы придерживаетесь DDD?Если вы - вы делаете инкапсуляцию нет-нет :-).Один агрегат никогда не должен редактировать другой агрегат.Если у вас есть то, что оба действительно Агрегированы, то они станут связанными (что совершенно «законно» в смысле DDD), но тогда ваши свойства в FooViewModel будут иметь тип BarViewModel, а не тип Bar. Таким образом, Barбудет (как и должно быть) отвечать за само обновление - и мы сохраняем ссылку только в FooViewModel.

Однако, если вы выполняете AggregateRoot с дочерним элементом ValueType, то вот что вы можете сделать, еслинемного измененная модель домена:

public class Foo
{
    public string SomeProperty { get; set; }
    public Bar Bar { get; set; }
    public void Save()
    {
        //Magically saves to persistent storage...
    }
}
public class Bar
{
    public Bar(string someOtherProperty)
    {
        SomeOtherProperty = someOtherProperty;
    }
    public string SomeOtherProperty { get; private set; }
}

А затем для ViewModels:

public class FooViewModel
{
    private Foo _foo;
    public FooViewModel()
    {
        Bar = new BarViewModel();
    }
    public BarViewModel Bar { get; private set; }
    public void SetFoo(Foo foo)
    {
        _foo = foo;
        SomeProperty = foo.SomeProperty;
        Bar.SetBar(foo.Bar);
    }
    public string SomeProperty { get; set; }
    public void SaveChanges()
    {
        _foo.SomeProperty = SomeProperty;
        _foo.Bar = Bar.CreateUpdatedBar();
        _foo.Save();
    }
}
public class BarViewModel
{
    public string SomeOtherProperty { get; set; }
    public void SetBar(Bar bar)
    {
        SomeOtherProperty = bar.SomeOtherProperty;
    }
    public Bar CreateUpdatedBar()
    {
        return new Bar(SomeOtherProperty);
    }
}

Таким образом, FooViewModel теперь может управлять BarViewModel (который ничего не делает, кроме как принимает значение типа -и создайте новый, когда его спросят.) Это также решает общую проблему UI («Как мы редактируем объект, у которого нет сеттеров?» - ответ: «Мы не создаем новый»). Многоотсутствует конкретизация (INotifyPropertyChanged, грязное отслеживание и т. д., но это легко, если вы преодолеете этот скачок мышления: -).

Надеюсь, в этом есть какой-то смысл :-) В противном случае я с удовольствием уточню.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...