【问题标题】:Adding Autofac to WPF MVVM application将 Autofac 添加到 WPF MVVM 应用程序
【发布时间】:2017-04-28 21:26:55
【问题描述】:

我似乎无法找到解决此问题的方法。我已经看到了几个关于这个的问题,但没有一个真正给我一个解决方案。我对 Autofac 完全陌生,并没有真正做过多少 WPF + MVVM,但知道基础知识。

我有一个 WPF 应用程序(使用 ModernUI for WPF),我正在尝试将 Autofac 添加到其中,我很难弄清楚如何在所有视图中解析我的服务,因为它们无法访问我的容器。我有一个主视图,这是我的入口点,在那里我设置了我的容器:

public partial class MainWindow : ModernWindow
{
    IContainer AppContainer;

    public MainWindow()
    {

        SetUpContainer();

        this.DataContext = new MainWindowViewModel();
        InitializeComponent();

        Application.Current.MainWindow = this; 
    }

    private void SetUpContainer()
    {
        var builder = new ContainerBuilder();

        BuildupContainer(builder);

        var container = builder.Build();

        AppContainer = container;
    }

    private void BuildupContainer(ContainerBuilder builder)
    {
        builder.RegisterType<Logger>().As<ILogger>();
        ...
    }
}

我遇到的问题是弄清楚如何解决我的其他视图中的记录器和其他服务,我通过 ViewModel 构造函数注入所有依赖项,如下所示:

public partial class ItemsView : UserControl
{
    private ItemsViewModel _vm;

    public ItemsView()
    {
        InitializeComponent();

        IFileHashHelper fileHashHelper = new MD5FileHashHelper();
        ILibraryLoader libraryLoader = new LibraryLoader(fileHashHelper);
        ILogger logger = new Logger();

        _vm = new ItemsViewModel(libraryLoader, logger);
        this.DataContext = _vm;
    }
}

有些视图有大量的注入参数,这就是我希望 Autofac 进来帮助我清理的地方。

我正在考虑将容器传递给 ViewModel 并将其作为属性存储在我的 ViewModelBase 类中,但我读到这将是一种反模式,即使那样我也不知道这是否会自动在其他 ViewModel 中解析我的对象。

我设法使用 Autofac 组装了一个简单的控制台应用程序

class Program
{
    static void Main(string[] args)
    {

        var builder = new ContainerBuilder();
        builder.RegisterType<Cleaner>().As<ICleaner>();
        builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();

        var container = builder.Build();

        using (var scope = container.BeginLifetimeScope())
        {

            ICleaner cleaner = container.Resolve<ICleaner>();
            cleaner.Update(stream);
        }
    }
}

但这很简单,因为它只有一个入口点。

我想了解如何将 Autofac 添加到我的 WPF 应用程序中。我确定我做错了什么。感谢您的帮助。

【问题讨论】:

标签: c# wpf mvvm dependency-injection autofac


【解决方案1】:

扩展我上面的评论:

我在所有 WPF MVVM 应用程序中都使用 Autofac,我相信它是更好的 DI 框架之一 - 这是我的观点,但我认为它是有效的。

另外对我来说,应该在 99% 的情况下避免使用 PRISM,它是一个“寻找问题的解决方案”,而且由于大多数人不会在 WPF 中构建动态可组合的运行时解决方案,因此不需要它,我相信人们会不同意。

像任何架构模式一样,应用程序生命周期有一个设置\配置阶段,简单地说,在您的情况下,在显示第一个视图(窗口)之前,将为依赖注入、日志记录、异常完成整个设置处理、Dispatcher 线程管理、主题等。

我有几个将 Autofac 与 WPF\MVVM 结合使用的示例,下面列出了几个,我想说看看 Simple.Wpf.Exceptions 示例:

https://github.com/oriches/Simple.Wpf.Exceptions

https://github.com/oriches/Simple.Wpf.DataGrid

https://github.com/oriches/Simple.MahApps.Template

【讨论】:

  • 这可能正是我正在寻找的。据我所知,您使用 App OnStartup 作为您的入口点并从那里构建您的容器。这对我来说似乎很容易做到。我看不清楚的是你如何从那里设置你的范围?您的范围在哪里处理?我指的是您的 Simple.Wpf.Exceptions 项目。
  • 是的,一切都从 OnStartup 开始,设置异常处理,启动引导程序 (DI) 和许多调试\诊断内容
  • 范围是什么意思?
  • 如果你的意思是范围 DI 容器中的范围,一般来说我不使用它们,我以前有过,但现在我发现我是否对架构和实现很勤奋(MVVM ) 那么我一般不需要它们。话虽如此,我确实提供了一个示例,说明我可能如何在 MVVM 应用程序中限定容器,但它已经很长时间没有更新了,所以 nuget 引用相当陈旧 - github.com/oriches/Simple.Wpf.Composition
  • 我在想为什么我最近在做 WPF\MVVM 时不想能够创建作用域容器,我得出的结论是——如果应用程序中的所有服务都注册为单例因为它们是无状态的,并且每个实例只注册 ViewModel,所以我不需要范围,因为服务将被共享并且 ViewModel 应该及时处理
【解决方案2】:

您可以使用与控制台应用程序类似的技术:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<Cleaner>().As<ICleaner>();
        builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();

        // Add the MainWindowclass and later resolve
        build.RegisterType<MainWindow>().AsSelf();

        var container = builder.Build();

        using (var scope = container.BeginLifetimeScope())
        {
            var main = scope.Resolve<MainWindow>();
            main.ShowDialog();
        }
    }
}

请务必使用 [STAThread] 标记 Main。然后在项目的属性中,在 Application 选项卡下,将 Startup object 设置为 Program 类。

但是,我不确定不运行 App.Run() 和运行 MainWindow.ShowDialog() 的影响。

要使用App.Run() 执行相同操作,请执行以下操作:

1) 从 App.xaml 中删除 StartupUri="MainWindow.xaml"

2) 将以下内容添加到 App.xaml.cs

protected override void OnStartup(StartupEventArgs e)
{
    var builder = new ContainerBuilder();
    builder.RegisterType<Cleaner>().As<ICleaner>();
    builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();

    // Add the MainWindowclass and later resolve
    build.RegisterType<MainWindow>().AsSelf();

    var container = builder.Build();

    using (var scope = container.BeginLifetimeScope())
    {
        var window = scope.Resolve<MainWindow>();
        window.Show();
    }
}

【讨论】:

  • 我猜您会将container 分配给MainWindow,以便它能够在必要时解析更多服务或其他窗口?
  • @t3chb0t 如果您需要MainWindow 中的其他服务,您可以将它们注入到构造函数中。
【解决方案3】:

WPF 没有自然的组合根或简单的 DI 集成。 Prism 是一组非常常见的库,专门用于为您搭建桥梁。

(这不是 Autofac 特定的 - 这是将 DI 添加到 WPF 应用程序的一般指导。)

【讨论】:

  • 我读过关于 Prism 的文章,并且还读到将它应用到现有应用程序(基本上是重写它)是一项艰巨的任务,所以我认为这不适合我的解决方案。我有一个工作应用程序,它完全是 DI,带有单元测试,但我希望使用 Autofac 之类的东西来清理注入的参数。
  • Prism 看起来很棒,谢谢。 @Ben - 如果您已经运行 DI 和测试,您可能会对“重写”更改的可实现性感到惊讶。以无需编写新测试(重构)的方式处理经过测试的代码库比您预期的要快得多。人们忘记了 TDD red-green-refactor 的“重构”部分是一个悲剧——这也是有趣的部分,最终结果是维护的乐趣。但是,如果您的团队成员不愿意学习框架,这很困难(因此在许多情况下不实用)。
猜你喜欢
  • 1970-01-01
  • 2013-08-16
  • 2018-08-08
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 2011-04-09
  • 1970-01-01
  • 2016-09-21
相关资源
最近更新 更多