WPF / MVVM: повторно использовать ViewModel в нескольких контроллерах и разделение проблем - PullRequest
0 голосов
/ 26 ноября 2010

В моем AdministrationController Я использую PupilViewModel , например:

_adminRepo.GetSchoolclassPupilList().ForEach(s =>
            {
                SchoolclassViewModel sVM = new SchoolclassViewModel(s, _adminRepo);

                foreach (PupilViewModel pVM in sVM.PupilListViewModel)
                {
                    pVM.Documents.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument);
                    pVM.Documents.AddDocumentDelegate += new Action(OnAddDocument);
                    pVM.Documents.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument);
                }
                SchoolclassList.Add(sVM);
            }); 

PupilViewModel создается таким образом:

public SchoolclassViewModel(Schoolclass schoolclass, IAdministrationRepository adminRepo)
        {
            _schoolclass = schoolclass;
            _adminRepo = adminRepo;  

            PupilListViewModel = new ObservableCollection<PupilViewModel>();
            schoolclass.Pupils.ForEach(p => PupilListViewModel.Add(new PupilViewModel(p, _adminRepo)));                      
        }

Как вы наверняка заметили, PupilViewModel принимает 2 параметра в своем конструкторе.Важным является второй параметр, который является специфичным для службы / хранилища экземпляром IAdministrationRepository.

Теперь существует еще один Контроллер с именем IncidentReportingController .В своем конструкторе я делаю то же самое, что и в AdministrationController:

// When I now try to wrap my pupils into a ViewModel I have a problem:

IEnumerable<Pupil> pupils = incidentRepo.GetPupilIncidentReportDocumentList();         
PupilViewModels = new ObservableCollection<PupilViewModel>(pupils.Select(p => new PupilViewModel(p, ???)));

A.) Либо я не хочу передавать службу в PupilViewModel, потому что нет причин обновлять свойство в PupilViewModel, так как онотолько для чтения в представлении.

B.) В моем AdministrationController я получаю данные из службы от этого Aggregation: 1 Schoolclass имеет N Pupils, а 1 Pupil имеет N Documents.Эти сущности заключены в SchoolclassViewmodels, PupilViewModels и DocumentViewModels ...

Теперь в моем IncidentController я тоже получаю данные из службы, и мой Aggregation очень похож: 1 Pupil имеет N IncidentReports и 1 IncidentReport имеет N Documents.Эти объекты обернуты в PupilViewModels, IncidentReportViewModels, DocumentViewModels ...

ПРОБЛЕМА is => В классе PupilViewModel он уже оборачивает DocumentListViewModel.Теперь мне нужно снова PupilViewModel, чтобы обернуть IncidentReportListViewModel, и позже у меня снова будет 1 Ученик имеет 1 SeatingChair и обернуть их снова.Это означает, что я должен передать ТРИ Сервиса PupilViewModel, хотя они мне не всегда нужны.

Мне трудно сразу перейти к решению проблемы, но почему-то я чувствую, что это не правильный путь.

Итак, как мне повторно использовать ту же модель ViewModel, которая переносит объекты с разными агрегатами, имеющими разные сервисы?

Ответы [ 3 ]

1 голос
/ 27 ноября 2010

Не зная, насколько далеко вы находитесь на пути ... Я настоятельно рекомендую вам взглянуть на Призма , в которой используется Единство .Как указано на веб-сайте Unitys ...

Блок приложений Unity (Unity) представляет собой облегченный расширяемый контейнер внедрения зависимостей с поддержкой внедрения конструктора, свойства и вызова метода.

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

Вы также можете, например, установить время жизни зарегистрированного типа, скажем, IDoSomethingController, в фактически тип, DoSomethingController ... и установите его время жизни в качестве Singleton, если вам нужно передать один экземпляр, когда кто-то запрашивает объект типа IDoSomethingController.

Как только вы используете эти платформы, вы больше не 'обновляя экземпляр, вы используете платформу для повторного использования, которое вы ищете.

IDoSomethingController controller = IUnityContainer.Resolve<IDoSomethingController>();

EDIT : так как вы заявили, что используете MEFedMVVM;структура DI существует.Ваш PupilViewModel является экземпляром ObservableCollection.Это излишне, пропустите это до конца.Модель ViewModel должна нести дополнительный вес, прежде чем вы просто отправите данные.Ваши ViewModels, кажется, пытаются представить объекты verusu концепции.Я имею в виду, что у вас может быть просто SchoolViewModel, который выставляет учеников, классы и т. Д. Эти элементы становятся моделями, которые могут быть агрегированы в той или иной форме в вашей модели представления.ViewModel предназначен для посредника в представлении.Он может содержать множество информации о различных моделях и службах, становясь единой точкой данных для View.

0 голосов
/ 27 ноября 2010

У вас, кажется, нет необходимости использовать репозиторий в вашей модели представления?Я ожидаю увидеть репозиторий для ваших бизнес-типов, а не для вашей модели представления.

Например ...

PubilsViewModel(ISchool school) { }

Для модели представления учеников просто потребуется школьный экземпляр, введенныйUnity (или выбранный вами IOC), все методы для сохранения, обновления и т. Д. Хранятся в бизнес-объекте.

var school = new School();
school.Save();
school.Update();

или даже в статических методах бизнес-объекта, которые принимают тип школы в своих конструкторах?В любом случае, я ожидал бы, что ваш режим просмотра будет вызывать методы для Business Objects, для объекта School более логично знать детали его сохранения (включая проверку, которую, возможно, необходимо выполнить, прежде чем в конечном итоге разрешить сохранение).в базу данных).

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

Пример:

public class School
{
   private ISchoolRepository _repository;

   public string Name { get; set; }

   public School()
   {
      this._repository = IoC.Resolve<ISchoolRepository>();
   }

   public bool IsValid()
   {
     // Some kind of business logic?
     if (this.Name != null)
     {
       return true;
     }

      return false;
   }

   public void Save()
   {
      if (this.isValid())
      {
         this._repository.Save(this)
      }
}

Хранилище будет обрабатывать любой код, необходимый для сохранения записи в базе данных.

Школьная организация не должна знать, КАК ее сохранить, нет причин, по которым она не может знатьдетали того, что нужно для того, чтобы спасти меняХотя ... ViewModel не нужно знать, что уровень детализации (и не должен), используя внедрение зависимостей, бизнес-объект (School) просто знает, что ему нужен ISchoolRepository, создает его экземпляр и затем вызывает методы для него.

Это решит вашу проблему ...

// Knows it needs a school.
// When needing to start the save process for a School, would call the methods on the
// school instance provided in the constructor.
PupilViewModel(School school) { }

// Knows it needs a Repository.
// Would perform validation of business rules and call methods on the repository when//
// it is ready to be pursisted.
School(ISchoolRepository repository) {}

// Repository
// Would perform the read / write of the data.
SchoolRepository() {}

Имеет ли это смысл?Надеюсь, вы сможете увидеть разделение проблем, которое вы начали пытаться достичь ... надеюсь, это поможет!

Бен

0 голосов
/ 27 ноября 2010

Я отвечу на свой вопрос, и те, кто следил за моим вопросом или ответили здесь, пожалуйста, протестируйте / проверьте мое решение и прокомментируйте, стоит ли решение:

Проблема происхождения состояла в том, что я повторно использовал PupilViewModel в 3 местах в другом контексте. Один раз, когда ctor PVM нуждается в услуге AdministrationService, для другого использования ему нужен сервис IncidentReportingService и в последний раз, когда я забыл третий сервис ...

Теперь вводить ВСЕ 3 услуги во ВСЕ 3 места мне кажется глупым:

PupilViewModel pVM = new PupilViewModel(pupil,adminService,IncidentReportingService,3rdService);

Выполнение этого 3 раза выглядит как плохая архитектура. На самом деле мне нужно это: Просто оберните ученика и введите все 3 услуги, КОГДА мне НУЖНО их действительно !!!

http://marlongrech.wordpress.com/2010/05/23/mefedmvvm-v1-0-explained/

Прокрутите вниз, ребята, и вы увидите, что MEFedMVVM может делать инъекции Ctor и Prop !!! Полностью пропущен этот параметр и его MEF-инъекция по умолчанию (Property Injection).

РЕДАКТИРОВАТЬ : Это не работает, из-за этого:

public SchoolclassViewModel(Schoolclass schoolclass, IAdministrationRepository adminRepo)
        {
            _schoolclass = schoolclass;
            _adminRepo = adminRepo;          

            //this.PropertyChangedHandler(object o, PropertyChangedEventArgs e)
            //{
            //    switch (e.PropertyName)
            //    {
            //        case "SchoolclassCode" : _saRepo.UpdateSchoolclass(_schoolclass); break;       
            //        default: break;
            //    }
            //}  

            PupilListViewModel = new ObservableCollection<PupilViewModel>();
            schoolclass.Pupils.ForEach(p => PupilListViewModel.Add(new PupilViewModel(p, _adminRepo)));
        }

        public IAdministrationRepository AdministrationRepository { get; set; }

Вы видите, как _adminRepo передается вновь созданному PupilViewModel? В то время, когда Ctor запущен, Property AdministrationRepository по-прежнему равен нулю, чтобы получить оттуда репо ... bad: /

...