Где разместить и настроить контейнер IoC в приложении WPF?

Я работаю над приложением WPF среднего размера (MVVM), которое в будущем должно быть расширяемым и поддерживаемым. Поэтому я решил использовать контейнер IoC (в данном случае Unity), чтобы сохранить гибкость.

Однако я не уверен, где разместить и настроить Unity в приложении WPF.

Я предполагаю, что контейнер должен быть доступен глобально, поэтому он, вероятно, должен перейти в класс Application. Но следует ли мне сделать это статическим свойством? Стоит ли настраивать его в обработчике событий Application_Startup ()?

Eg:

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
    public static UnityContainer MyUnityContainer;


    private void Application_Startup(object sender, StartupEventArgs e)
    {
        // instantiate and configure Unity
    }
}

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

App.MyUnityContainer

Я предполагаю, что это один из способов сделать это, но я не уверен, есть ли лучшие практики для этой проблемы, особенно для приложений WPF.


person matori82    schedule 29.04.2012    source источник


Ответы (3)


Взгляните на корневой шаблон композиции. Что вы хотите сделать, так это инициализировать его в обработчике событий Startup и забыть о его существовании для остальной части приложения.

Вы пытаетесь реализовать шаблон локатора служб, который, по мнению многих является второстепенным решением этой проблемы.

person Filippo Pensalfini    schedule 30.04.2012
comment
Как я могу полностью забыть о его существовании? Вы же захотите разрешить зависимости на самом верхнем уровне вашего графа зависимостей, не так ли? Я имею в виду, например, что обработчик события нажатия кнопки должен сохранять Customer в базу данных, которая использует CustomerRepository. Разве вам не придется разрешать CustomerRepository, чтобы использовать его, поэтому снова понадобится контейнер IoC? - person johnildergleidisson; 16.10.2012
comment
@JoaoMilasch Самый высокий уровень? Абсолютно. Но почему верхний уровень должен быть окном, а не классом приложения? В вашем случае у вас есть окно, требующее репозитория, который можно ввести с помощью Constructor Injection. Затем вы можете разрешить MainWindow в событии Startup вашего приложения, вызывающего контейнер. - person Filippo Pensalfini; 16.10.2012
comment
Ага! Вы ответили на мой вопрос косвенно, и теперь все имеет смысл. Я думал, что мне нужно настроить контейнер в классе App, но Windows потребуется прямой доступ к настроенному контейнеру. Вместо этого, если я сделаю то, что вы предлагаете, объект-контейнер должен будет существовать только в классе App. Поправьте меня, если я ошибаюсь, пожалуйста! :) Спасибо! - person johnildergleidisson; 16.10.2012
comment
@JoaoMilasch, вот в чем идея! Если вы хотите копнуть глубже, взгляните на эту книгу, я продолжаю рекомендовать его, потому что он ответил на многие мои вопросы о DI. - person Filippo Pensalfini; 16.10.2012

Позвольте мне опубликовать то, что я пришел к выводу, и, надеюсь, это поможет людям. Поправьте, если что не так! :П

Я предполагаю, что мы будем искать что-то вроде этого:

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
    private void Application_Startup(object sender, StartupEventArgs e)
    {
        UnityContainer myUnityContainer = new UnityContainer();
        //make sure your container is configured
        myUnityContainer.RegisterType<ISomeDependency, SomeDependencyImplementation>();
        myUnityContainer.RegisterType<IMainWindow, MainWindow>();

        myUnityContainer.Resolve<IMainWindow>().Show();
    }
}

public partial class MainWindow : Window, IMainWindow
{
    private ISomeDependency _someDependency;

    public MainWindow(ISomeDependency someDependency)
    {
        _someDependency = someDependency;
    }
}

Обратите внимание, что нет глобальных объектов или синглтонов, контейнер существует до тех пор, пока работает MainWindow, и все зависимости за этой точкой входа далее в граф композиции разрешаются автоматически, пока контейнер знает о них.

person johnildergleidisson    schedule 16.10.2012
comment
Я думаю, вам нужен RegisterType вместо Register. - person Jacob Brewer; 08.05.2014
comment
@JoaoMilasch отсутствует одна часть и (для меня непонятно) как контейнер продвигается в основной, что-то похожее на то, что вы можете иметь в asp.net MVC с помощью GlobalConfiguration.Configuration.DependencyResolver. Resolve проверяет только наличие ссылки на все зависимости в контейнере. - person stenly; 15.09.2014
comment
мент как лучший по всей структуре приложения - person stenly; 15.09.2014
comment
@stenly Resolve создает ссылку в соответствии с конфигурацией контейнера. Вызывая RegisterType, я говорю, что для интерфейса IMainWindow создайте объект, когда я вызываю Resolve для этого интерфейса, который в данном случае является экземпляром класса MainWindow. MainWindow однако зависит от ISomeDependency (см. Конструктор). Затем я также настроил контейнер для разрешения этой зависимости, когда это необходимо. Он автоматически создаст экземпляр класса SomeDependencyImplementation и передаст его конструктору также на основе моей конфигурации. - person johnildergleidisson; 15.09.2014
comment
В этом примере я помещаю контейнер в точку входа приложения. Граф композиции (дерево зависимостей) будет разрешен контейнером, если я предоставил конфигурацию для каждой отдельной зависимости, которая есть в этом конкретном графе. Будьте такой конфигурацией, чтобы создавать экземпляр класса каждый раз, когда он требуется (RegisterType), или использовать существующий экземпляр (RegisterInstance). - person johnildergleidisson; 15.09.2014

Согласно новой версии контейнера Unity, мы также должны зарегистрировать его собственный экземпляр, чтобы получить его в моделях представления с помощью внедрения конструктора.

Файл App.xaml.cs:

protected override void OnStartup(StartupEventArgs e)
{
       var unityIoC = new UnityContainer();
       unityIoC.RegisterTypes(AllClasses.FromAssembliesInBasePath(), WithMappings.FromMatchingInterface, WithName.Default);
       unityIoC.RegisterInstance(typeof(IUnityContainer), unityIoC);
}

Просмотр класса модели

[InjectionConstructor]
public MyViewModel(IUnityContainer container)
{
}

Теперь контейнер единства будет доступен для нас в модели представления и может использоваться для разрешения.

person Arul    schedule 28.10.2017