Я использую инъекцию зависимости конструктора в своем приложении 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, но все еще используется для компоновки объектов?