将Autofac容器传递给WPF UserControl

Kev*_*ski 3 c# wpf dependency-injection autofac

我正在使用autofac解析WPF应用程序中的Views和ViewModels。IComponentContext被自动传递到视图中。

一个例子:

    public BusinessAuto(int proposedCoverageId, IComponentContext componentContext)
    {
        DataContext = componentContext.Resolve<BusinessAutoViewModel>(new TypedParameter(typeof(Int32), proposedCoverageId));
        InitializeComponent();
    }
Run Code Online (Sandbox Code Playgroud)

在此视图的XAML中,正在创建具有自己的ViewModel的UserControl。一个例子:

<userControl:AdditionalCoveragesControl Margin="0,10"/>
Run Code Online (Sandbox Code Playgroud)

Autofac不会创建UserControl(视图是),因此Autofac无法将依赖项注入UserControl的构造函数中。

如何将对IComponentContext的引用获取到初始视图的XAML中声明的UserControl中?

我觉得我要么需要Autofac以某种方式创建我的UserControl,要么需要恢复到不鼓励使用的Global静态容器(ick),或者我必须使用DependencyProperty将容器向下传递(也请单击ick)。

Jer*_*all 5

我不会注入(实际上是有效的)容器,因为这是一种称为服务定位器的劣等形式的控制反转,其缺陷可以由您的当前情况总结:您最终需要将容器注入所有内容。

相反,您需要从“此组件负责创建什么,它需要做什么?”的角度来解决问题。

正如Lonni-Loki所述,一种选择是注入一个完全形成的控件,但我对此持不同意见:如果主视图有责任创建此子组件,则应创建它-但是为了促进这种责任,您应该向主视图中注入服务/模型/等,然后将其传递或用于创建它。Autofac的工厂方法存根非常适合此操作:

例如,如果子视图需要IFooViewModel,则可以向容器注入Func <IFooViewModel <(在上下文中注册的工厂方法),然后可以将其用于“按需提供”新包含的视图。

(或Func <arg1,arg2等,IFooViewModel>,根据您的需要)

一个方便的经验法则是,在考虑类X时,首先将“新的”内容带到任何地方,然后将其传递给构造函数。现在看一下代码,并问自己:“如果我想要类X的实例,我需要传递什么构造函数?” 这些是您的依赖项。

让我们举一个更动手的例子……假设您具有这样的结构:

  • 应用程序创建MainWindow

  • MainWindow创建SubView1,需要IMainWindowViewModel

  • SubView1需要ISubView1Model,IFooService

  • SubView1创建SubView2

  • SubView2需要ISubView2Model,IBarService

因此,我们的ctor看起来像:

public MainWindow(IMainWindowViewModel viewModel, 
     Func<SubView1> subView1Factory)

public SubView1(ISubView1Model viewModel,
    IFooService fooService,
    Func<IFooService, SubView2> subView2Factory)

public SubView2(
    ISubView2ModelViewModel viewModel, 
    IBarService barService)
Run Code Online (Sandbox Code Playgroud)

设置容器时,您会遇到类似这样的情况:

(请注意,我使用了多种IoC容器,因此我的Autofac语法可能会生锈)

var builder = new ContainerBuilder();

// Simple bit, register implementations for viewmodel, services
builder.RegisterType<MainWindowViewModel>.As<IMainWindowViewModel>();
builder.RegisterType<SubView1Model>.As<ISubView1Model>();
builder.RegisterInstance<FooService>.As<IFooService>();

// ok, lemme see if I can remember expression syntax...

// Simple case: 'static' resolution of subview
// We want a func that takes no args and returns us a fully-initialized
//  SubView1
builder.Register<Func<SubView1>>(context =>
{
    // Since all the bits of a subview1 are registered, simply
    // resolve it and return
    var view = context.Resolve<SubView1>();
    return () => view;
});

// Complicated case - lets say this viewmodel depends
// on foo service, which it uses to determine which 
// bar service to use
builder.Register<Func<IFooService, SubView2>>(context =>
{
    // and our view model
    var vm = context.Resolve<ISubView2ViewModel>();

    return (service) =>
    {
        var barService = new BarService(service);
        return new SubView2(vm, barService);
    };
});
Run Code Online (Sandbox Code Playgroud)

IoC容器的荣耀(仅在我看来)用法是“您根据我已经告诉您的内容弄清楚如何获取所有数据”部分-否则,您最好在此处使用手动注入手动传递依赖项。就是说,我们潜在的MainWindow构造现在是:

container.Resolve<MainWindow>();
Run Code Online (Sandbox Code Playgroud)

希望我在代码中不要输入太多错误或错误,但是我有一段时间没有使用Autofac了。

  • 这正是我想要的。我希望Autofac Wiki如此清晰。我感谢您的帮助。 (2认同)