Сохранение использования 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.