Сохранение использования DI-контейнера в корне композиции в Silverlight и MVVM
Мне не совсем понятно, как я могу проектировать, поэтому я сохраняю ссылку на DI-контейнер в корне композиции для приложения Silverlight + MVVM.
У меня есть следующий простой сценарий использования: есть основной вид (возможно, список элементов) и действие, чтобы открыть представление редактирования для одного элемента. Поэтому основной вид должен создавать и показывать вид редактирования, когда пользователь предпринимает действие (например, нажимает на какую-то кнопку).
Для этого у меня есть следующий код:
public interface IView
{
IViewModel ViewModel {get; set;}
}
Затем для каждого представления, которое мне нужно для создания, у меня есть абстрактный factory, например
public interface ISomeViewFactory
{
IView CreateView();
}
Этот factory затем объявляется зависимостью модели родительского представления, например:
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 = ????
}
}
"????" part - моя проблема, потому что модель представления для представления должна быть разрешена из DI-контейнера, чтобы он вносил свои зависимости. Я не знаю, как это сделать, не имея зависимости от DI-контейнера в любом месте, кроме корня композиции.
Одним из возможных решений могло бы быть либо зависимость от модели представления, которая вводится в factory, например:
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-контейнере, и это только они и корень композиции, которые имеют эту зависимость.
Ответы
Ответ 1
Чтобы максимально приблизить ваш примерный код, вы можете ввести еще один уровень косвенности в виде 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, придерживаясь принципа Single Responsibility. В определенной степени это также пример Агрегирование сервисов.
Теперь вы можете реализовать 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, но выше это одно из возможных решений.
Ключ состоит в том, чтобы продолжать извлекать абстракции, пока у каждого не будет четко определенной ответственности. Это упражнение действительно не связано с тем, что код Container-agnostic вообще отсутствует, но в конечном итоге должен придерживаться принципов SOLID.