Область видимости объекта контейнера IoC
Я заинтересован в реализации контейнера IoC в своем проекте, но я не видел там примера, который бы делал то, что мне нужно.
Вот ситуация, мое приложение встроено в WPF и использует шаблон MVVM для создания проигрывателя для инфракрасного видео формата. В этом формате каждое видео фактически состоит из нескольких "подкадров" (под этим подразумевается захват с несколькими выдержками одновременно для увеличения динамического диапазона данных), каждое из которых отображается в TabControl. Я хотел бы использовать контейнер IoC, чтобы помочь мне создать ViewModels для каждой из вкладок, но у меня есть две проблемы.
Мне нужен способ передать объект, представляющий подкадр, для которого я создаю ViewModel, поэтому по крайней мере одна из зависимостей ViewModel не может быть создана контейнером IoC, потому что он уже существует.
У меня есть пользовательские элементы управления внутри представления для подкадра, которые имеют свои собственные 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);
}