将 Autofac 容器传递给 WPF UserControl
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13845903/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pass Autofac container to WPF UserControl
提问by Kevin Kalitowski
I am using autofac to resolve Views and ViewModels in a WPF application. IComponentContext is being passed into the View automatically.
我正在使用 autofac 来解析 WPF 应用程序中的视图和视图模型。IComponentContext 被自动传递到视图中。
An example:
一个例子:
public BusinessAuto(int proposedCoverageId, IComponentContext componentContext)
{
DataContext = componentContext.Resolve<BusinessAutoViewModel>(new TypedParameter(typeof(Int32), proposedCoverageId));
InitializeComponent();
}
In the XAML for this view there are UserControls being created that have their own ViewModels. An example:
在此视图的 XAML 中,创建了具有自己的 ViewModel 的 UserControl。一个例子:
<userControl:AdditionalCoveragesControl Margin="0,10"/>
Autofac is not creating the UserControl (the View is) so Autofac cannot inject dependencies into the UserControl's constructor.
Autofac 没有创建 UserControl(View 是),因此 Autofac 无法将依赖项注入 UserControl 的构造函数中。
How can I get the reference to IComponentContext into a UserControl that is declared in the initial View's XAML?
如何将对 IComponentContext 的引用获取到在初始视图的 XAML 中声明的 UserControl 中?
I feel that I either need Autofac to somehow create my UserControl, that I need to revert to the discouraged Global static container (ick), or I have to use a DependencyProperty to pass the container down (also ick).
我觉得我要么需要 Autofac 以某种方式创建我的 UserControl,我需要恢复到不鼓励的全局静态容器(ick),或者我必须使用 DependencyProperty 将容器向下传递(也是 ick)。
回答by JerKimball
I wouldn't inject (what is effectively) your container, as this is an inferior form of inversion of control called Service Locator, with deficiencies that can be summed up by your current situation: you end up needing to inject the container into EVERYTHING.
我不会注入(实际上是什么)您的容器,因为这是一种称为 Service Locator 的低级控制反转形式,其不足之处可以通过您当前的情况来总结:您最终需要将容器注入到一切中。
Instead, you need to approach the problem from the point of view of "What is this component in charge of creating, and what does it need to do it?"
相反,您需要从“这个组件负责创建什么,它需要做什么?”的角度来处理问题。
As mentioned by Lonni-Loki, one option is to inject a fully formed control but I disagree on that point: if the main view has the responsibility of creating this sub component, then it should create it - but to facilitate that responsibility, you should inject the main view with the services/models/etc it would then need to pass along or otherwise use to create it. Autofac's factory method stubs are ideal for this:
正如 Lonni-Loki 所提到的,一种选择是注入一个完全形成的控件,但我不同意这一点:如果主视图有责任创建这个子组件,那么它应该创建它——但为了促进这个责任,你应该使用服务/模型/等注入主视图,然后它需要传递或以其他方式用于创建它。Autofac 的工厂方法存根非常适合:
For example if the sub view needs an IFooViewModel, you can inject the container with a Func<IFooViewModel< (a factory method registered with the context), which it can then use to "feed on demand" the new contained view.
例如,如果子视图需要 IFooViewModel,您可以使用 Func<IFooViewModel<(在上下文中注册的工厂方法)注入容器,然后它可以使用它来“按需提供”新包含的视图。
(or a Func<arg1, arg2, etc, IFooViewModel>, as your needs require)
(或 Func<arg1, arg2, etc, IFooViewModel>,根据您的需要)
One handy rule of thumb is when considering class X, first take anywhere you "new up" anything and instead pass that into the constructor. Now look at the code and ask yourself "If I wanted an instance of class X, what would I need to pass the constructor?" Those are your dependencies.
一个方便的经验法则是在考虑类 X 时,首先在任何地方“新建”任何东西,然后将其传递给构造函数。现在查看代码并问自己“如果我想要一个 X 类的实例,我需要通过构造函数传递什么?” 这些是您的依赖项。
Let's take a more hands-on example...say you've got a structure like this:
让我们举一个更实际的例子......假设你有一个这样的结构:
Application creates MainWindow
MainWindow creates SubView1, needs IMainWindowViewModel
SubView1 needs ISubView1Model, IFooService
SubView1 creates SubView2
SubView2 needs ISubView2Model, IBarService
应用程序创建主窗口
MainWindow 创建 SubView1,需要 IMainWindowViewModel
SubView1 需要 ISubView1Model、IFooService
SubView1 创建 SubView2
SubView2 需要 ISubView2Model、IBarService
So our ctors would look like:
所以我们的ctors看起来像:
public MainWindow(IMainWindowViewModel viewModel,
Func<SubView1> subView1Factory)
public SubView1(ISubView1Model viewModel,
IFooService fooService,
Func<IFooService, SubView2> subView2Factory)
public SubView2(
ISubView2ModelViewModel viewModel,
IBarService barService)
When setting up your container, you'd have something like so:
设置容器时,您会看到如下内容:
(note, I use a variety of IoC containers, so my Autofac syntax might be rusty)
(注意,我使用了各种 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);
};
});
The glorious (and in my opinion, only) use for IoC containers is the "You figure out how to get all the bits based on what I've already told you" part - otherwise, you might as well use manual injection, where you pass in dependencies by hand. That said, our potentially complicated construction of MainWindow is now just:
IoC 容器的光荣(在我看来,唯一)使用是“您根据我已经告诉您的内容找出如何获取所有位”部分 - 否则,您不妨使用手动注入,在那里您手动传递依赖项。也就是说,我们可能复杂的 MainWindow 构造现在只是:
container.Resolve<MainWindow>();
I hope I didn't make too many typos/errors in the code, but I haven't used Autofac for a while.
我希望我没有在代码中犯太多错别字/错误,但我有一段时间没有使用 Autofac。
回答by Lonli-Lokli
You should not pass componentContext to BusinessAuto view, instead you should pass AdditionalCoveragesControl.
您不应将 componentContext 传递给 BusinessAuto 视图,而应传递 AdditionalCoveragesControl。
public BusinessAuto(int proposedCoverageId, BusinessAutoViewModel vm, AdditionalCoveragesControl view)
{
...
DataContext = vm;
InternalView = view;
}
XAML:
XAML:
<ContentPresenter Content="{Binding InternalView}" Margin="0,10"/>
Then all your views will be independent from Autofac, you should just register all of them in container.
那么你的所有视图都将独立于 Autofac,你应该将它们全部注册到容器中。

