Область видимости объекта контейнера IoC

Я заинтересован в реализации контейнера IoC в своем проекте, но я не видел там примера, который бы делал то, что мне нужно.

Вот ситуация, мое приложение встроено в WPF и использует шаблон MVVM для создания проигрывателя для инфракрасного видео формата. В этом формате каждое видео фактически состоит из нескольких "подкадров" (под этим подразумевается захват с несколькими выдержками одновременно для увеличения динамического диапазона данных), каждое из которых отображается в TabControl. Я хотел бы использовать контейнер IoC, чтобы помочь мне создать ViewModels для каждой из вкладок, но у меня есть две проблемы.

  1. Мне нужен способ передать объект, представляющий подкадр, для которого я создаю ViewModel, поэтому по крайней мере одна из зависимостей ViewModel не может быть создана контейнером IoC, потому что он уже существует.

  2. У меня есть пользовательские элементы управления внутри представления для подкадра, которые имеют свои собственные ViewModel, поэтому эти ViewModel также должны быть созданы контейнером IoC. Проблема в том, что, хотя элементы управления (и их ViewModel) имеют отдельные проблемы, они не являются полностью независимыми, поэтому они используют некоторые объекты координации. К сожалению, во всех примерах, которые я видел, вы можете иметь свой IoC-контейнер для создания нового экземпляра зависимости или иметь синглтон, но мне нужен один экземпляр во время контролируемого периода времени, когда я создаю подкадр ViewModel,

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

Что у меня сейчас

В открытом коде фильма:

foreach (var subframe in movieFile)
{
    // Subframes is the ObservableCollection that the TabControl is bound to
    Subframes.Add(new SubframeViewModel(subframe));
}

В SubframeViewModel:

public SubframeViewModel(ISubframe subframe)
{
    _subframe = subframe;

    // FrameController tracks the current frame number and fires an event 
    // when it changes
    var frameController = new FrameController(subframe); 

    // ImageViewModel represents the actual movie image. Uses FrameController
    // to know when to raise PropertyChanged for the property that represents
    // the image
    ImageViewModel = new ImageViewModel(subframe, frameController);

    // FrameControlViewModel represents the playback controls.  Uses
    // FrameController to implement actions
    // Play, Pause, Jump to frame, Jump to time...
    FrameControlViewModel = new FrameControlViewModel(subframe, frameController);
}

Что бы я хотел получить, не меняя существующую семантику

В открытом коде фильма:

foreach (var subframe in movieFile)
{
    Subframes.Add(container.Resolve<SubframeViewModel>());
}

В SubframeViewModel:

public SubframeViewModel(ISubframe subframe, ImageViewModel imageModel, 
                            FrameControlViewModel frameModel)
{
    _subframe = subframe;

    ImageViewModel = imageModel;

    FrameControlViewModel = frameModel;
}

На самом деле, существует больше координации и объектов ViewModel, но шаблоны одинаковы. Тем не менее, я думаю, вы можете понять, почему я заинтересован в контейнере IoC здесь.

Я думаю, что мой сценарий должен быть довольно распространенным, но я не уверен, и я не хочу тратить свое время, пытаясь вставить квадратный колышек в круглое отверстие, так что вот мои вопросы. Может ли любой / все контейнеры IoC сделать это? Если нет, можете ли вы указать мне на рефакторинг, который улучшит мой код и заставит работать IoC?

2 ответа

Autofac определенно делает то, что вам нужно - параметры, предоставленные явно, могут быть объединены с автоматически связанными контейнером:

vm = container.Resolve<SubFrameViewModel>(new NamedParameter("subframe", subframe));

(Также возможно сопоставление по типу, а не по имени.)

Вы даже можете сделать так, чтобы контейнер вставлял Func или пользовательский делегат в вызывающий компонент, так что зависимость от контейнера (т.е. вызов Resolve) не нужна:

Func<ISubFrame, SubFrameViewModel> _vmFactory; // Injected
vm = _vmFactory(subframe);

См. http://code.google.com/p/autofac/wiki/DelegateFactories для получения информации о последнем.

Ник

Похоже, вы хотите обработать дополнительные параметры конструктора в вашем сценарии IoC, и я вижу два варианта:

1 - Создание простого конструктора, который принимает подкадр и представляет ImageViewModel и FrameControlViewModel как общедоступные настраиваемые свойства, например, так:

// assumes frameControlVM and imageVM have been constructed and are valid
foreach (var subframe in movieFile)
{
    var subframeVM = container.Resolve<SubframeViewModel>(subframe);
    subframeVM.ImageVM = imageVM;
    subframeVM.FrameControlVM = frameControlVM ;
    Subframes.Add(subframeVM);
}

2 - Передайте необходимые аргументы в контейнер IoC, который просто передает параметры в конструктор SubframeVM:

// assumes frameControlVM and imageVM have been constructed and are valid
foreach (var subframe in movieFile)
{
    var subframeVM = container.Resolve<SubframeViewModel>(subframe, imageVM, frameControlVM);
    Subframes.Add(subframeVM);
}

В конечном итоге, какой из них вы выберете, зависит от того, насколько тесно вы хотите, чтобы ваш IoC-преобразователь SubframeViewModel находился с остальными виртуальными машинами. Если ваши imageVM и frameControlVM нуждаются в собственных IoC, вы можете просто связать их вместе:

// assumes frameControlVM and imageVM have been constructed and are valid
foreach (var subframe in movieFile)
{
    var frameControlVM = container.Resolve<FrameControlViewModel >(subframe);
    var imageVM = container.Resolve<ImageViewModel>(subframe, frameControlVM);
    var subframeVM = container.Resolve<SubframeViewModel>(subframe, imageVM, frameControlVM);
    Subframes.Add(subframeVM);
}
Другие вопросы по тегам