Autofac报告不存在的循环依赖项

时间:2010-11-15 21:17:32

标签: autofac

我最近将Autofac添加到一个大型的现有应用程序来管理DI。

在这个过程中,我用一个由容器管理的单个实例替换了单例,这个实例被注入到依赖的构造函数中。但是,在某些情况下,必须打破循环依赖关系。我发现这样做的最简单方法是利用 OnActivated 事件。我们打算修改这些类型以消除循环依赖关系,但现在更改它们的风险太大了。

对于循环依赖关系中涉及的类型,我添加了一个名为 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 个答案:

答案 0 :(得分:6)

IComponentContext正在呼叫的DomainObjectFactory似乎是在单个解析操作期间创建的临时版本,例如cbuilder.Register(c => new DomainObjectFactory(c)) 参数类似于:

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

这些不是线程安全的;正确的代码是:

c

这是一个令人讨厌的问题,偶尔会出现,但通常是可以检测到的,因为一旦Resolve()被释放,ObjectDisposedException调用就会抛出告密者{{1}}。我将在您链接到的并发页面上做一个注释。