dotnet dependency_injection ai_generated true

System.InvalidOperationException: A circular dependency was detected for the service of type 'X'.

ID: dotnet/di-circular-dependency

Also available as: JSON · Markdown
87%Fix Rate
89%Confidence
80Evidence
2023-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
8 active

Root Cause

The built-in .NET DI container detects circular dependencies at resolution time when service A requires service B, and service B (directly or transitively) requires service A. This commonly occurs in layered architectures where services grow bidirectional dependencies, or when event/notification handlers reference the services that trigger them. Fix by breaking the cycle with Lazy<T>, refactoring to introduce a mediator or interface segregation, or extracting shared logic into a third service.

generic

Workarounds

  1. 85% success Inject Lazy<T> to defer resolution of one side of the cycle
    Register a Lazy<T> factory for one of the circular services: builder.Services.AddScoped<Lazy<IServiceB>>(sp => new Lazy<IServiceB>(() => sp.GetRequiredService<IServiceB>())); Then inject Lazy<IServiceB> instead of IServiceB in ServiceA's constructor. The actual resolution is deferred until .Value is accessed, which breaks the construction-time cycle. This is a tactical fix; refactoring is preferred long-term
  2. 90% success Extract shared logic into a third service to break the dependency cycle
    Identify the shared functionality that causes both services to depend on each other. Extract it into a new service (e.g., ISharedLogic) that both ServiceA and ServiceB depend on, instead of depending on each other. This converts the circular A<->B dependency into a tree: A->C and B->C. This is the cleanest architectural solution
  3. 88% success Use the Mediator pattern to decouple event-driven circular dependencies
    When the cycle exists because ServiceA notifies ServiceB and ServiceB calls back to ServiceA, introduce MediatR or a simple IMediator interface. ServiceA publishes an event through the mediator, and ServiceB handles it without directly referencing ServiceA: builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); Replace direct service calls with _mediator.Send(new MyCommand()) or _mediator.Publish(new MyEvent())

Dead Ends

Common approaches that don't work:

  1. Switching from constructor injection to property injection to hide the cycle 85% fail

    The built-in .NET DI container does not support property injection. Even with third-party containers (Autofac, etc.) that support it, property injection merely defers the cycle resolution and creates fragile initialization order dependencies where services may be used before their properties are set

  2. Registering one of the services as Transient to avoid the cycle detection 95% fail

    Changing the service lifetime does not affect cycle detection. The DI container analyzes the constructor dependency graph regardless of lifetime. A transient registration still requires constructing the full dependency chain, which will still be circular

  3. Using IServiceProvider directly in the constructor to manually resolve the dependency 80% fail

    Resolving the circular dependency from IServiceProvider inside the constructor triggers the same cycle detection because the resolution happens during construction. This is the Service Locator anti-pattern and it moves the exception from compile-time-detectable to runtime without solving the underlying design issue

Error Chain

Leads to:
Preceded by:
Frequently confused with: