【问题标题】:Autofac reporting circular dependencies that don't existAutofac 报告不存在的循环依赖项
【发布时间】:2010-11-15 21:17:32
【问题描述】:

我最近将 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 模块中注册,使用 Load 方法如下:

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 获取详细问题 :)

标签: autofac


【解决方案1】:

DomainObjectFactory 调用的 IComponentContext 似乎是在单个解析操作期间创建的临时对象,例如c 参数类似于:

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

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

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

这是一个不时出现的令人讨厌的问题,但通常是可以检测到的,因为一旦处理了cResolve() 调用就会抛出ObjectDisposedException 的信号。我会在您链接到的并发页面上做一个记录。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-13
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 2020-11-18
    相关资源
    最近更新 更多