我尝试使用Autofac实现以下依赖注入方案。
假设我有三个类,A,B和P.A类包含P类型的属性,并且还有对B的引用,需要在运行时注入。
class A
{
private B _b;
public P Prop { get; set; }
InitializeProp()
{ ... }
}
同时,B类依赖于P的实例:
class B
{
private P _p;
}
现在,最重要的细节:我需要以某种方式向B注入一个P的实例。但是,该实例必须是来自A类的Prop,只有在A中的InitializeProp()被调用之后才会初始化。假设InitializeProp()可以在对象的生命周期中稍后调用,而不是A的构造函数。
这种情况是否可以通过Autofac实现?
我尝试过这样的事情,但是我得到了一个循环引用异常,大概是当B进一步解决时:
containerBuilder.RegisterType<A>().SingleInstance();
containerBuilder.Register<Func<P>>(c =>
{
var a = c.Resolve<A>();
return () => a.Prop;
});
答案 0 :(得分:2)
为了能够掌握注册问题,我经常发现在代码中手工构建所需的对象图是有用的。如果不使用DI容器,这就是您希望实现的目标:
var p = new P();
var a = new A(new B(p)) { P = p };
您可以看到,P
的实例在整个图表中被重用以实现此目的。有了这些知识,我们现在可以确定您需要做什么才能实现您想要的目标。
因此,解决方案是将Autofac配置为重用P
的相同实例。由于您将A
注册为SingleInstance
,因此除P
以SingleInstance
注册外,别无其他选择,否则您将处理Captive Dependency。
但如果我们后退一步,我认为P
没有理由成为A
的财产。事实上,使用属性注入有hardly ever a good reason。
您应该始终使用构造函数注入。所以你的课程应如下所示:
class A { public A(B b, P p) { } }
class B { public B(P p) { } }
这完全消除了配置问题,因为您只需执行以下操作:
containerBuilder.RegisterType<A>().SingleInstance();
containerBuilder.RegisterType<B>().SingleInstance();
containerBuilder.RegisterType<P>().SingleInstance();
答案 1 :(得分:0)
所以最终我弄清楚了,原来Autofac比我想象的要聪明。
没有必要明确注册P的Func
,而是简化注册:
builder.RegisterType<A>().SingleInstance();
builder.Register(c =>
{
var a = c.Resolve<A>();
return a.Property;
});
builder.RegisterType<B>();
现在在主程序中我可以执行以下操作:
var a = container.Resolve<A>();
a.InitializeProperty();
a.Execute(); // calls B