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)。
我不会注入(实际上是有效的)容器,因为这是一种称为服务定位器的劣等形式的控制反转,其缺陷可以由您的当前情况总结:您最终需要将容器注入所有内容。
相反,您需要从“此组件负责创建什么,它需要做什么?”的角度来解决问题。
正如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了。