2010-11-15 69 views
4

我最近將Autofac添加到一個大型的現有應用程序來管理DI。Autofac報告不存在的循環依賴關係

在這個過程中,我用被注入到從屬構造函數的容器管理的單個實例替換了單例。然而,在某些情況下,必須打破循環依賴。我發現最簡單的方法是利用OnActivated事件。我們打算修改這些類型以消除循環依賴,但現在改變它們的風險太大了。

要參與循環依賴的類型,I添加了一個名爲方法ResolveCircularDependencies(這使得它明顯的是,這種方法只被暫時使用,並且用於解決這些循環的目的)。此方法在OnActivated事件中被調用。

所以我的代碼現在看起來像這樣:

public class ServiceA 
{ 
    private ServiceB otherService; 

    public ServiceA() 
    { 
     ... 
    } 

    public void ResolveCircularDependencies(ServiceB other) 
    { 
     this.otherService = other; 
    } 

    public void SomeMethod() 
    { 
     ... 
     this.otherService.SomeMethod(); 
     ... 
    } 
} 

public class ServiceB 
{ 
    private ServiceA otherService; 

    public ServiceB() 
    { 
     ... 
    } 

    public void ResolveCircularDependencies(ServiceA other) 
    { 
     this.otherService = other; 
    } 

    public void SomeMethod() 
    { 
     ... 
     this.otherService.SomeMethod(); 
     ... 
    } 
} 

這些類型註冊的Autofac模塊中,與負載方法如下:

public override void Load(ContainerBuilder builder) 
{ 
    builder 
     .Register(ctx => new ServiceA()) 
     .OnActivated(e => e.Instance.ResolveCircularDependences(e.Context.Resolve<ServiceB>())); 

    builder 
     .Register(ctx => new ServiceB()) 
     .OnActivated(e => e.Instance.ResolveCircularDependences(e.Context.Resolve<ServiceA>())); 
} 

這似乎在正常工作在大多數情況下。但是,我們隨機眼看Autofac認爲自己已經找到了循環依賴,並與異常返回下面的堆棧跟蹤:

at Autofac.Core.Resolving.CircularDependencyDetector.CheckForCircularDependency(IComponentRegistration registration, Stack`1 activationStack, Int32 callDepth) 
at Autofac.Core.Resolving.ResolveOperation.Resolve(ISharingLifetimeScope activationScope, IComponentRegistration registration, IEnumerable`1 parameters) 
at Autofac.Core.Resolving.ComponentActivation.Resolve(IComponentRegistration registration, IEnumerable`1 parameters) 
at Autofac.ResolutionExtensions.TryResolve(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) 
at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Service service, IEnumerable`1 parameters) 
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters) 
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context) 
at DomainObjectFactory.Resolve[T]() 
at DomainObjectFactory.BuildMyObject() 

我們也隨機看到以下錯誤:

at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) 
at System.Collections.Generic.Stack`1.Enumerator.MoveNext() 
at System.Linq.Enumerable.Count[TSource](IEnumerable`1 source, Func`2 predicate) 
at Autofac.Core.Resolving.CircularDependencyDetector.IsCircularDependency(IComponentRegistration registration, Stack`1 activationStack) 
at Autofac.Core.Resolving.CircularDependencyDetector.CheckForCircularDependency(IComponentRegistration registration, Stack`1 activationStack, Int32 callDepth) 
at Autofac.Core.Resolving.ResolveOperation.Resolve(ISharingLifetimeScope activationScope, IComponentRegistration registration, IEnumerable`1 parameters) 
at Autofac.Core.Resolving.ComponentActivation.Resolve(IComponentRegistration registration, IEnumerable`1 parameters) 
at Autofac.ResolutionExtensions.TryResolve(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) 
at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Service service, IEnumerable`1 parameters) 
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters) 
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context) 
at DomainObjectFactory.Resolve[T]() 
at DomainObjectFactory.BuildMyObject() 

這些正在發生在所有註冊完成之後(在應用程序啓動時發生在單個線程上)。可以在單獨的線程上同時調用BuildMyObject方法。然而,根據Autofac wiki,這似乎是可以接受的。

我已經檢查了ServiceA和ServiceB的完整依賴樹,並且對象樹中沒有循環。

有沒有人看到這種行爲?分辨率是多少?

我們使用Autofac 2.3.2.632-NET35作爲發佈下載here

+1

+1的詳細問題:) – 2011-01-12 17:33:23

回答

6

您的DomainObjectFactory調用的IComponentContext看起來是在單個解析操作(例如, c參數類似於:

builder.Register(c => new DomainObjectFactory(c)) 

這些不是線程安全的;正確的代碼是:

builder.Register(c => new DomainObjectFactory(c.Resolve<IComponentContext>()) 

它是現在,然後出現一個討厭的疑難雜症,但通常檢測的,因爲一旦c設置在Resolve()通話將拋出告密ObjectDisposedException。我會在鏈接到的併發頁面上做一個記錄。

+0

謝謝。這正是我所做錯的。 – 2010-11-16 14:21:21