![](/img/trans.png)
[英]DependencyResolutionException Circular component dependency detected: How to use Autofac to inject decorator?
[英]Decorator for multiple interfaces - a circular dependency riddle in Autofac
我來自Ninject,但我決定嘗試Autofac,因為它似乎更積極地開發。 到目前為止,我可以說使用.WhenInjectedExactlyInto
語法注冊裝飾器並不像在Ninject中那么容易。 無論如何,請耐心等待我,因為我是一個Autofac新手。
這是問題所在:
我有A
實現接口IA
,由A_Decorator
。 A_Decorator
實現接口IA
和IB
,並且應該由AB_Decorator
修飾, AB_Decorator
也同時實現IA
和IB
。 AB_Decorator
接受類型IA
和IB
兩個依賴項(因此它是兩者的裝飾器)但它們都應該解析為A_Decorator
的相同實例。 它看起來像這樣: AB_Decorator(A_Decorator(A) as IA, A_Decorator(A) as IB)
。 從Autofac容器請求類型IA
或類型IB
服務時,它們應引用單個AB_Decorator
實例。
通過單詞描述有點棘手,但這是我能想出的最簡單的代碼示例,它顯示了這種情況(我已經向構造函數添加了實例ID和跟蹤消息以查看發生了什么):
using System;
using Autofac;
namespace AutofacExample
{
internal interface IA { }
internal interface IB { }
class A : IA
{
static int _instanceCounter;
readonly int Id = ++_instanceCounter;
public A()
{
Console.WriteLine(this);
}
public override string ToString()
{
return $"{GetType().Name}[{nameof(Id)}={Id}]";
}
}
class A_Decorator : IA, IB
{
static int _instanceCounter = 10;
readonly int Id = ++_instanceCounter;
/* decorated1 should reference instance of A */
public A_Decorator(IA decoratedA)
{
Console.WriteLine($"{this}({nameof(decoratedA)}={decoratedA})");
}
public override string ToString()
{
return $"{GetType().Name}[{nameof(Id)}={Id}]";
}
}
class AB_Decorator : IA, IB
{
static int _instanceCounter = 100;
readonly int Id = ++_instanceCounter;
/* Both decorated1 and decorated2 should reference the same instance of A_Decorator */
public AB_Decorator(IA decoratedA, IB decoratedB)
{
Console.WriteLine($"{this}({nameof(decoratedA)}={decoratedA}, {nameof(decoratedB)}={decoratedB})");
}
public override string ToString()
{
return $"{GetType().Name}[{nameof(Id)}={Id}]";
}
}
class Program
{
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder
.RegisterType<A>()
.Named<IA>(nameof(A))
.SingleInstance();
builder
.RegisterType<A_Decorator>()
.Named<IA>(nameof(A_Decorator))
.Named<IB>(nameof(A_Decorator))
.SingleInstance();
builder
.RegisterType<AB_Decorator>()
.Named<IA>(nameof(AB_Decorator))
.Named<IB>(nameof(AB_Decorator))
.SingleInstance();
/* A is decorated by A_Decorator as IA */
builder
.RegisterDecorator<IA>(
(c, decorated) =>
c.ResolveNamed<IA>(nameof(A_Decorator), TypedParameter.From(decorated)),
nameof(A))
//.Keyed<IA>("innerA")
//.Keyed<IB>("innerB")
.SingleInstance();
/* Trying to register AB_Decorator as IA creates circular dependency */
//builder
// .RegisterDecorator<IA>(
// (c, decorated) =>
// c.ResolveNamed<IA>(nameof(AB_Decorator), TypedParameter.From(decorated)),
// "innerA")
// .SingleInstance();
/* A_Decorator is decorated by AB_Decorator as IB */
builder
.RegisterDecorator<IB>(
(c, decorated) =>
c.ResolveNamed<IB>(nameof(AB_Decorator), TypedParameter.From(decorated)),
nameof(A_Decorator) /* "innerB" */)
.SingleInstance();
IContainer container = builder.Build();
IA a = container.Resolve<IA>();
IB b = container.Resolve<IB>();
Console.WriteLine($"{nameof(a)} == {nameof(b)} ? {ReferenceEquals(a, b)}");
Console.WriteLine($"{nameof(a)} is {a.GetType().Name}");
Console.WriteLine($"{nameof(b)} is {b.GetType().Name}");
}
}
}
不幸的是,請求IA
實例給了我A_Decorator
,而對於IB
我獲得了AB_Decorator
。 嘗試取消注釋額外的裝飾器注冊塊會導致循環依賴性異常( DependencyResolutionException: Circular component dependency detected: System.Object -> AutofacExample.AB_Decorator -> System.Object -> AutofacExample.AB_Decorator
)並且我無法嘗試各種組合已命名的注冊。
有誰知道解決這個問題? 提前致謝。
問題在於AB_Decorator
的裝飾器注冊。 特別是解析AB_Decorator
的lambda函數:
( c, decorated ) => c.ResolveNamed<IA>( nameof( AB_Decorator ), TypedParameter.From( decorated ) );
AB_Decorator
的構造AB_Decorator
接受2個參數,這兩個參數都應該是A_Decorator
的相同實例, A_Decorator
作為decorated
提供給lambda。 然而, decorated
被作為參數傳遞通過僅一次TypedParameter.From( decorated )
。 因此,Autofac將嘗試通過容器解析第二個參數。
現在的注冊IB
表明,我們應該得到一個單一實例A_Decorator
包裹在AB_Decorator
。 因此,要解析IB
,容器必須構造AB_Decorator
。 有問題,我們目前正在嘗試將AB_Decorator
解析為IA
,但我們需要一個IB
來完成為IA
構造的AB_Decorator
的構造函數參數。 IB
在容器中注冊為AB_Decorator
。 所以你得到:
AB_Decorator(A_Decorator(A) as IA, AB_Decorator(A_Decorator(A) as IA, AB_Decorator(etc...))
我們需要傳遞decorated
成解決當兩個參數AB_Decorator
。 像這樣:
builder
.RegisterDecorator<IA>(
( c, decorated ) =>
c.ResolveNamed<IA>( nameof( AB_Decorator ),
new TypedParameter( typeof( IA ), decorated ),
new TypedParameter( typeof( IB ), decorated )
)
,"innerA"
)
.SingleInstance();
builder
.RegisterDecorator<IB>(
( c, decorated ) =>
c.ResolveNamed<IB>( nameof( AB_Decorator ),
new TypedParameter( typeof( IA ), decorated ),
new TypedParameter( typeof( IB ), decorated )
)
, nameof( A_Decorator ) /* "innerB" */
)
.SingleInstance();
現在我們將decorated
的A_Decorator
發送到IA
和IB
參數。 直接構造TypedParameter
實例允許我在參數列表中指定我希望實例實現的類型,在本例中為AB_Decorator
。
干得好:
ContainerBuilder builder = new ContainerBuilder();
builder
.RegisterType<A>()
.Named<IA>(nameof(A))
.SingleInstance();
builder
.RegisterType<A_Decorator>()
.Named<IA>(nameof(A_Decorator))
.Named<IB>(nameof(A_Decorator))
.WithParameter(new ResolvedParameter((pi, c) => pi.Name == "decoratedA",
(pi, c) => c.ResolveNamed<IA>(nameof(A))))
.SingleInstance();
builder
.RegisterType<AB_Decorator>()
.As<IA, IB>()
.WithParameter(new ResolvedParameter((pi, c) => pi.Name == "decoratedA",
(pi, c) => c.ResolveNamed<IA>(nameof(A_Decorator))))
.WithParameter(new ResolvedParameter((pi, c) => pi.Name == "decoratedB",
(pi, c) => c.ResolveNamed<IB>(nameof(A_Decorator))))
.SingleInstance();
IContainer container = builder.Build();
打印:
A[Id=1]
A_Decorator[Id=11](decoratedA=A[Id=1])
AB_Decorator[Id=101](decoratedA=A_Decorator[Id=11], decoratedB=A_Decorator[Id=11])
a == b ? True
a is AB_Decorator
b is AB_Decorator
API令人困惑,因為在這種情況下你不需要RegisterDecorator()
(它用於一次裝飾一整套組件)。
(如果我們可以烘焙整個食物會很好:
.WithParameter(new ResolvedParameter((pi, c) => pi.Name == "decoratedA",
(pi, c) => c.ResolveNamed<IA>(nameof(A))))
在Autofac中使用更簡單的WithParameter()
重載; 如果你在這里看到勝利,我認為這是一個很好的建議來提升項目的問題跟蹤器。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.