我正在使用autofac来解析WPF应用程序中的Views和ViewModel。 IComponentContext将自动传递到View中。
一个例子:
public BusinessAuto(int proposedCoverageId, IComponentContext componentContext)
{
DataContext = componentContext.Resolve<BusinessAutoViewModel>(new TypedParameter(typeof(Int32), proposedCoverageId));
InitializeComponent();
}
在此视图的XAML中,正在创建具有自己的ViewModel的UserControl。一个例子:
<userControl:AdditionalCoveragesControl Margin="0,10"/>
Autofac没有创建UserControl(View是),因此Autofac无法将依赖项注入UserControl的构造函数。
如何将对IComponentContext的引用转换为在初始View的XAML中声明的UserControl?
我觉得我要么需要Autofac以某种方式创建我的UserControl,我需要恢复到不鼓励的全局静态容器(ick),或者我必须使用DependencyProperty来传递容器(也是ick)。
答案 0 :(得分:5)
我不会注入(有效)你的容器,因为这是一种称为服务定位器的控制反转的劣质形式,缺点可以通过你当前的情况来总结:你最终需要注入容器进入一切。
相反,您需要从“负责创建的这个组件是什么,以及它需要做什么?”的角度来解决问题。
正如Lonni-Loki所提到的,一种选择是注入一个完全形成的控件,但我不同意这一点:如果主视图有责任创建这个子组件,那么它应该创建它 - 但是为了促进这个责任,你应该注入主视图与服务/模型/等,然后需要传递或以其他方式创建它。 Autofac的工厂方法存根是理想的:
例如,如果子视图需要IFooViewModel,则可以使用Func&lt; IFooViewModel&lt; (在上下文中注册的工厂方法),然后它可以用来“按需提供”新包含的视图。
(或Func&lt; arg1,arg2等,IFooViewModel&gt;,根据您的需要)
一个方便的经验法则是在考虑X类时,首先在任何地方“新建”任何东西,然后将其传递给构造函数。现在看看代码并问自己“如果我想要一个X类的实例,我需要传递什么构造函数?”那些是你的依赖。
让我们举一个更实际的例子......说你有这样的结构:
应用程序创建MainWindow
MainWindow创建SubView1,需要IMainWindowViewModel
SubView1需要ISubView1Model,IFooService
SubView1创建SubView2
SubView2需要ISubView2Model,IBarService
所以我们的ctors会是这样的:
public MainWindow(IMainWindowViewModel viewModel,
Func<SubView1> subView1Factory)
public SubView1(ISubView1Model viewModel,
IFooService fooService,
Func<IFooService, SubView2> subView2Factory)
public SubView2(
ISubView2ModelViewModel viewModel,
IBarService barService)
设置容器时,你会有这样的事情:
(注意,我使用各种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);
};
});
IoC容器的光荣(在我看来,只有)使用的是“你弄清楚如何根据我已经告诉过你的东西获取所有的部分” - 否则,你不妨使用手动注射,您手动传递依赖项。也就是说,我们可能很复杂的MainWindow构建现在只是:
container.Resolve<MainWindow>();
我希望代码中没有出现过多的拼写错误/错误,但我暂时没有使用过Autofac。
答案 1 :(得分:2)
您不应将componentContext传递给BusinessAuto视图,而应传递AdditionalCoveragesControl。
public BusinessAuto(int proposedCoverageId, BusinessAutoViewModel vm, AdditionalCoveragesControl view)
{
...
DataContext = vm;
InternalView = view;
}
<强> XAML:强>
<ContentPresenter Content="{Binding InternalView}" Margin="0,10"/>
然后你的所有观点将独立于Autofac,你应该只在容器中注册它们。