Я использую инжекцию зависимости конструктора в своем приложении WPF и продолжаю работать по следующему шаблону, поэтому хотел бы узнать мнение других людей и услышать об альтернативных решениях.
Цель состоит в том, чтобы подключить иерархию ViewModel к аналогичной иерархии моделей, чтобы ответственность за представление информации в каждой модели лежала на ее собственной реализации ViewModel. (Шаблон также возникает при других обстоятельствах, но MVVM должен послужить хорошим примером.)
Вот упрощенный пример. Учитывая, что у меня есть модель, у которой есть коллекция других моделей:
public interface IPerson
{
IEnumerable<IAddress> Addresses { get; }
}
public interface IAddress
{
}
Я хотел бы отразить эту иерархию в ViewModel, чтобы я мог связать ListBox (или любой другой) с коллекцией в Person ViewModel:
public interface IPersonViewModel
{
ObservableCollection<IAddressViewModel> Addresses { get; }
void Initialize();
}
public interface IAddressViewModel
{
}
Дочерний ViewModel должен представить информацию из дочерней Модели, поэтому она вводится через конструктор:
public class AddressViewModel : IAddressViewModel
{
private readonly IAddress _address;
public AddressViewModel(IAddress address)
{
_address = address;
}
}
Вопрос в том, как лучше всего передать дочернюю модель соответствующей дочерней модели ViewModel?
Пример тривиален, но в типичном реальном случае ViewModels имеют больше зависимостей - каждая из которых имеет свои зависимости (и так далее). Я использую Unity 1.2 (хотя я думаю, что вопрос актуален для других контейнеров IoC), и я использую стратегии представления Caliburn для автоматического поиска и подключения соответствующего View к ViewModel.
Вот мое текущее решение:
Родительский ViewModel должен создать дочерний ViewModel для каждой дочерней Модели, поэтому в конструктор добавлен фабричный метод, который он использует во время инициализации:
public class PersonViewModel : IPersonViewModel
{
private readonly Func<IAddress, IAddressViewModel> _addressViewModelFactory;
private readonly IPerson _person;
public PersonViewModel(IPerson person,
Func<IAddress, IAddressViewModel> addressViewModelFactory)
{
_addressViewModelFactory = addressViewModelFactory;
_person = person;
Addresses = new ObservableCollection<IAddressViewModel>();
}
public ObservableCollection<IAddressViewModel> Addresses { get; private set; }
public void Initialize()
{
foreach (IAddress address in _person.Addresses)
Addresses.Add(_addressViewModelFactory(address));
}
}
Заводской метод, удовлетворяющий интерфейсу Func<IAddress, IAddressViewModel>
, зарегистрирован в основном UnityContainer
. Фабричный метод использует дочерний контейнер для регистрации зависимости IAddress
, требуемой ViewModel, а затем разрешает дочерний ViewModel:
public class Factory
{
private readonly IUnityContainer _container;
public Factory(IUnityContainer container)
{
_container = container;
}
public void RegisterStuff()
{
_container.RegisterInstance<Func<IAddress, IAddressViewModel>>(CreateAddressViewModel);
}
private IAddressViewModel CreateAddressViewModel(IAddress model)
{
IUnityContainer childContainer = _container.CreateChildContainer();
childContainer.RegisterInstance(model);
return childContainer.Resolve<IAddressViewModel>();
}
}
Теперь, когда PersonViewModel
инициализируется, он проходит через каждый Address
в Модели и вызывает CreateAddressViewModel()
(который был введен с помощью аргумента Func<IAddress, IAddressViewModel>
). CreateAddressViewModel()
создает временный дочерний контейнер и регистрирует модель IAddress
, чтобы при разрешении IAddressViewModel
из дочернего контейнера AddressViewModel
получал правильный экземпляр, внедренный через его конструктор.
Мне кажется, это хорошее решение, так как зависимости ViewModels очень ясны, и они легко тестируемы и не знают о контейнере IoC. С другой стороны, производительность в порядке, но невелика, так как можно создать много временных дочерних контейнеров. Также я получаю много очень похожих заводских методов.
- Это лучший способ внедрить дочерние модели в дочерние модели ViewModel с Unity?
- Есть ли лучший (или более быстрый) способ сделать это в других контейнерах IoC, например Autofac
- Как бы решить эту проблему с MEF, учитывая, что это не традиционный контейнер IoC, но все еще используется для создания объектов?