Мне не совсем ясно, как я могу разработать так, я сохраняю ссылку на контейнер DI в составе, поддерживают Silverlight + приложение MVVM.
У меня есть следующий простой сценарий использования: существует основное представление (возможно, список объектов) и действие для открытия представления редактирования для одного единственного объекта. Таким образом, основное представление должно создать и показать представление редактирования, когда пользователь принимает меры (например, нажимает некоторую кнопку).
Для этого у меня есть следующий код:
public interface IView
{
IViewModel ViewModel {get; set;}
}
Затем для каждого представления, что я должен смочь создать, у меня есть абстрактная фабрика, как так
public interface ISomeViewFactory
{
IView CreateView();
}
Эта фабрика затем объявляется зависимостью "родительской" модели представления, как так:
public class SomeParentViewModel
{
public SomeParentViewModel(ISomeViewFactory viewFactory)
{
// store it
}
private void OnSomeUserAction()
{
IView view = viewFactory.CreateView();
dialogService.ShowDialog(view);
}
}
Таким образом, все хорошо до здесь, никакой контейнер DI в поле зрения :). Теперь прибывает реализация ISomeViewFactory:
public class SomeViewFactory : ISomeViewFactory
{
public IView CreateView()
{
IView view = new SomeView();
view.ViewModel = ????
}
}
"????" часть является моей проблемой, потому что модель представления для представления должна быть разрешена от контейнера DI, таким образом, это ввело свои зависимости. То, что я не знаю, - то, как я могу сделать это, не имея зависимости к контейнеру DI нигде кроме корня состава.
Одно возможное решение состояло бы в том, чтобы иметь любого зависимость от модели представления, которая введена в фабрику, как так:
public class SomeViewFactory : ISomeViewFactory
{
public SomeViewFactory(ISomeViewModel viewModel)
{
// store it
}
public IView CreateView()
{
IView view = new SomeView();
view.ViewModel = viewModel;
}
}
В то время как это работает, это имеет проблему, что, так как целый граф объектов обеспечен электричеством "статически" (т.е. "родительская" модель представления получит экземпляр SomeViewFactory, который получит экземпляр SomeViewModel, и они будут жить пока "родительские" жизни модели представления), введенная реализация модели представления с сохранением информации и если пользователь откроет дочернее представление дважды, во второй раз, то когда модель представления будет тем же экземпляром и иметь состояние до. Я предполагаю, что мог работать вокруг этого с "Инициализировать" методом или чем-то подобным, но это не пахнет совершенно правильным.
Другое решение могло бы состоять в том, чтобы перенестись, контейнер DI и иметь фабрики зависят от обертки, но это все еще был бы контейнер DI, "скрытый" там :)
PS: мое текущее решение состоит в том, что фабрики знают о контейнере DI, и это - только они и корень состава, которые имеют эту зависимость.
Чтобы максимально приблизиться к вашему примеру кода, вы можете ввести еще один уровень косвенности в форме IViewPopulator:
public interface IViewPopulator
{
void Populate(IView view);
}
Теперь вы можете реализовать свой SomeViewFactory следующим образом:
public class SomeViewFactory : ISomeViewFactory
{
private readonly IViewPopulator populator;
public SomeViewFactory(IViewPopulator populator)
{
if (populator == null)
{
throw new ArgumentNullException("populator");
}
this.populator = populator;
}
public IView CreateView()
{
IView view = new SomeView();
this.populator.Populate(view);
return view;
}
}
Это разделяет создание представлений и совокупность ViewModels, придерживаясь принципа единой ответственности . В определенной степени это также пример агрегирования сервисов .
Теперь вы можете реализовать IViewPopulator как конкретный тип, который принимает обычные зависимости:
public class SomeViewPopulator : IViewPopulator
{
private readonly IDependency dep;
public SomeViewPopulator(IDependency dep)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
this.dep = dep;
}
public void Populate(IView view)
{
var vm = // Perhaps use this.dep to create an instance of IViewModel...
view.ViewModel = vm;
}
}
Вероятно, будут другие способы моделирования отношений между IView и IViewModel, но приведенное выше представляет одно из возможных решений.
Главное - продолжать извлекать абстракции до тех пор, пока у каждой из них не будет четко определена ответственность. Это упражнение на самом деле не о том, чтобы сделать код независимым от контейнера, а о соблюдении принципов SOLID.