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 ProviderService
depends 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:
ProviderService
requiresAnimalBuilderDto
AnimalBuilderDto
requiresProviderService
ProviderService
requiresAnimalBuilderDto
- ... Circular dependency, thus my application crashes.
So, I've investigated this for a while, and I've come up with these solutions.
- 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.
- 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.