Home > database >  Dependency Injection circular dependency when using a Provider for interface implementations
Dependency Injection circular dependency when using a Provider for interface implementations

Time:05-19

I'm currently facing an issue for which I have found some partial solutions, but not the one that actually makes me believe I cannot do better.

So, to put in simple, I'm using the Dependency Injection, and I'm facing a circular reference error. Here's something similar to the code I am using.

First, I have a IMyBuilder interface:

public interface IMyBuilder { }

Then, I have an abstract class that implements the interface, and a bunch of class extending the abstract one:

public abstract class MyBuilderBase<DTO> : IMyBuilder 
{
    public abstract Dto GetDto();
}

public class UserBuilderDto : MyBuilderDto<UserDTO> { ... }
public class ProfessorBuilderDto : MyBuilderDto<ProfessorDTO> { ... }
public class AnimalBuilderDto : MyBuilderDto<AnimalDTO> { ... }

Then, I have a ProviderService, which helps me get the right IMyBuilder based on the DTO type. This ProviderServicedepends on ALL the IMyBuilder, like so:

public class ProviderService
{
    private readonly IEnumerable<IMyBuilder> _builders;

    public ProviderService(IEnumerable<IMyBuilder> builders)
    {
        _builders = builder;
    }

    public IMyBuilder GetBuilder<DTO>()
    {
        return _builders.OfType<MyBuilderBase<DTO>>().FirstOrDefault();
    }
}

Finally, last piece of my code is the registration of the services, like so:

services.AddScoped<IMyBuilder, UserBuilderDto>();
services.AddScoped<IMyBuilder, ProfessorBuilderDto>();
services.AddScoped<IMyBuilder, AnimalBuilderDto>();
services.AddScoped<ProviderService>();

Notice that I register my builder as IMyBuilder: this way I can use the parameter IEnumerable<IMyBuilder> in the constructor of ProviderService.

Ok, now that we have all the inputs, here's the problem: inside the abstract method MyBuilderBase.GetDto I might need some other builder! Therefore, MyBuilderBase needs ProviderService, something like this:

public class AnimalBuilderDto : MyBuilderDto<AnimalDTO>
{
    private readonly ProviderService _providerService;

    public AnimalBuilderDto(ProviderService providerService)
        => (_providerService) = (providerService)

    public AnimalDto GetDto()
    {
        // using _providerService
    }
}

At this point you can clearly see where is my problem:

  1. ProviderService requires AnimalBuilderDto
  2. AnimalBuilderDto requires ProviderService
  3. ProviderService requires AnimalBuilderDto
  4. ... Circular dependency, thus my application crashes.

So, I've investigated this for a while, and I've come up with these solutions.

  1. LAZY INITIALIZATION IN PROVIDER SERVICE

This would be something like here: https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-circular-dependencies-in-net-core/

Basically, in the constructor of ProviderService I would not directly need to initialize the single IBuilderDto, avoiding the circular dependency..

What I don't like is that it seems a workaround, and not a solution. Also, if by any chance I put a breakpoint into providerService constructor, and inspect the lazy property, the application crashes.. Yeah, workaround.

  1. METHOD INJECTION

I would need to change the signature of the abstract method as follow:

public abstract Dto GetDto(ProviderService providerService);

This way, I would not need the AnimalBuilderDto to depends on ProviderService, REMOVING the circular dependency. This solution seems a real solution (it removes the circular dependency), but it adds a cost, which is the need of bringing this instance of ProviderService along all the application in order to use it when calling GetDto.


Here's end my investigation. Though, both solution does not seems to fix my problem! Ok, the second one does actually fix the problem, but create another one, which is the need of bringing the instance across all method calls.

Question is: is there any better solution to make a provider class like my ProviderService being reusable inside the same classes that it exploses?

CodePudding user response:

Create an event in consumer class and fire it , once fired make the server class return its current instance

CodePudding user response:

One other option to solve this is to move the functionality that is used in GetDto into another class that can be injected into both the builders and the ProviderService. This way, the circular dependency is removed and both classes still have access to the functionality.

Basically, from

ProviderService -> Builders -> ProviderService

to

ProviderService -> Builders -> SharedFunctions
                -> SharedFunctions

Whether or not the Builders and the ProviderService use the same instance of SharedFunctions then depends on the scope that you register it in the IoC container.

  • Related