Я использую инъекцию зависимости конструктора в своем приложении WPF, и я продолжаю работать в следующем шаблоне, поэтому хотел бы получить мнение других людей и услышать об альтернативных решениях.
Цель состоит в том, чтобы связать иерархию ViewModels с аналогичной иерархией моделей, чтобы ответственность за представление информации в каждой модели была связана с ее собственной реализацией ViewModel. (Образец также появляется при других обстоятельствах, но MVVM должен сделать хороший пример.)
Вот упрощенный пример. Учитывая, что у меня есть модель, в которой есть коллекция следующих моделей:
public interface IPerson
{
IEnumerable<IAddress> Addresses { get; }
}
public interface IAddress
{
}
Я хотел бы отразить эту иерархию в моделях ViewModels, чтобы я мог привязать ListBox (или что-то еще) к коллекции в Man 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 для каждой дочерней модели, поэтому он добавляет метод factory к своему конструктору, который он использует во время инициализации:
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));
}
}
A factory, который удовлетворяет интерфейсу Func<IAddress, IAddressViewModel>
, регистрируется с основным UnityContainer
. Метод factory использует дочерний контейнер для регистрации зависимости 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. С другой стороны, производительность в порядке, но не велика, так как многие временные дочерние контейнеры могут быть созданы. Также я получаю много очень похожих методов factory.
- Это лучший способ вставить дочерние модели в дочерние ViewModels с Unity?
- Есть ли лучший (или более быстрый) способ сделать это в других контейнерах IoC, например. Autofac?
- Как решить эту проблему с помощью MEF, учитывая, что он не является традиционным контейнером IoC, но все еще используется для компоновки объектов?