Как использовать инжекцию зависимости конструктора для подачи моделей из коллекции в их модели представления? - PullRequest
10 голосов
/ 10 мая 2010

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

Ответы [ 2 ]

2 голосов
/ 10 мая 2010

В зависимости от контейнера вы не можете указать параметр (именованный или иным образом) в методе CreateAddressViewModel вашей фабрики?

container.Resolve<IAddressViewModel>(new NamedParameterOverloads() { { "Address", model } };

В зависимости от контейнера вашей фабрике, возможно, придется знать имя параметра (TinyIoC и Castle afaik), или он может быть последним в списке зависимостей конструктора (YMMV в зависимости от контейнеров) , что не очень хорошо, но позволяет сэкономить большое количество дочерних контейнеров в быстрой последовательности, и последующий триггер GC, и вы все равно получаете DI для всех остальных зависимостей.

Конечно, это не работает, если у вашей виртуальной машины также есть зависимость, для которой требуется тот же IAddress, в этом случае, вероятно, вам нужен дочерний контейнер, если вы не хотите, чтобы виртуальная машина знала о контейнере.

Обновление: Если вы используете подконтейнер контейнера, который использует «последние выигрыши регистра» (что, я думаю, делает Unity), то вы можете каждый раз передавать один и тот же контейнер child в вашу Фабрику, и ваша фабрика просто зарегистрируйте новый IAddress - таким образом, вы не будете создавать новый экземпляр UnityContainer в куче для каждой итерации, и это должно сократить сборку мусора, если вы создаете много элементов.

0 голосов
/ 14 мая 2010

Пример приложения ViewModel WPF Application Framework (WAF) показывает, как можно объединить Модель и ViewModel. В примере используется MEF в качестве основы для внедрения зависимостей.

...