为什么使用依赖注入容器?

时间:2013-06-06 09:08:38

标签: wpf design-patterns dependency-injection composition

我已经完成了Karl Shiffet的'InTheBox' WPF Training,并发现它是学习WPF的绝佳资源。它确实提出的一件事是使用依赖注入和Unity容器。以下是为我提出一些问题的代码部分:

public partial class App : Application {

    protected override void OnStartup(StartupEventArgs e) {

        IUnityContainer container = new UnityContainer();

        container.RegisterType<IDialogService, ModalDialogService>(
            new ContainerControlledLifetimeManager());

        container.RegisterType<IEventRepository, EventRepository>(
            new ContainerControlledLifetimeManager());

        MainWindow window = container.Resolve<MainWindow>();

        window.DataContext = container.Resolve<MainWindowViewModel>();

        window.Show();
    }
}

依赖项正在注册到UnityContainer中,然后由UnityContainer注入到MainWindowViewModel中。我的问题是为什么要使用容器?为什么不使用下面的代码来实现与依赖注入相同的事情:

protected override void OnStartup(StartupEventArgs e)
{
    IDialogService dialogService = new ModalDialogService();
    IEventRepository eventRepository = new EventRepository();

    MainWindow window = new MainWindow();
    window.DataContext = 
        new MainWindowViewModel(eventRepository, dialogService);
    window.Show();
}

我仍然从组合根注入构造函数中的依赖项,因此我认为在这种情况下使用UnityContainer没有任何好处。

我很欣赏它显然存在是有原因但有人可以解释在这种情况下它是否添加了什么?另外,还有另一种情况,使用像这样的容器真的是一个明智的选择吗?

2 个答案:

答案 0 :(得分:5)

在这样一个简单的情况下使用DI容器对你来说并不是很重要。当事情变得更复杂时它开始变得更有意义,并且它也最小化依赖变化的影响。

比如说,您有一个ILoggingService,您的所有依赖项现在都使用。当使用像Unity这样的DI容器时,你只需要添加一行代码。

    protected override void OnStartup(StartupEventArgs e)
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<IDialogService, ModalDialogService>();
        container.RegisterType<IEventRepository, EventRepository>();
        container.RegisterType<ILoggingService, LoggingService>(); // added

        MainWindow window = container.Resolve<MainWindow>();
        window.DataContext = container.Resolve<MainWindowViewModel>();
        window.Show();
    }

当你自己动手时,你必须添加一行代码,并修改3行代码。

    protected override void OnStartup(StartupEventArgs e)
    {
        ILoggingService loggingService = new LoggingService(); // added
        IDialogService dialogService = new ModalDialogService(loggingService); // modified
        IEventRepository eventRepository = new EventRepository(loggingService); // modified

        MainWindow window = new MainWindow();
        window.DataContext = new MainWindowViewModel(eventRepository, dialogService, loggingService); // modified
        window.Show();
    }

当使用可以扫描要注册的类型的更高级容器时,您可能不必更改组合根目录中的任何代码。以下是使用AutoFac的示例。

    protected override void OnStartup(StartupEventArgs e)
    {
        var builder = new ContainerBuilder();
        var assembly = Assembly.GetExecutingAssembly();
        builder.RegisterAssemblyTypes(assembly)
               .AsSelf()
               .AsImplementedInterfaces();
        var container = builder.Build();

        MainWindow window = container.Resolve<MainWindow>();
        window.DataContext = container.Resolve<MainWindowViewModel>();
        window.Show();
    }

答案 1 :(得分:2)

你说得好。你的第二个例子是使用Mark Seemann所说的“穷人的DI”。它仍然是DI,但你自己就是这样做的。

当您开始管理许多不同类型的注入时,IoC容器就会自成一体,并且按照惯例管理和按类型注册类型等功能会成为大量的节省劳动力。

对于具有最小依赖关系管理的较小任务,正如您所建议的那样,它们可能过度。

如果你想了解更多,我会强烈推荐Seemann的书和博客。恕我直言,他比任何人都更好地解释了这个话题。