Reputation: 77
I have two classes, AccountService
:
public class AccountService : IAccountService
{
private readonly UserManager<CustomIdentityUser> _userManager;
private readonly SignInManager<CustomIdentityUser> _signInManager;
private readonly RoleManager<CustomIdentityRole> _roleManager;
private readonly IUserRepository _userRepository;
private readonly LinkGenerator _linkGenerator;
private readonly IHttpContextAccessor _accessor;
private readonly IMediaService _mediaService;
public AccountService(
UserManager<CustomIdentityUser> userManager,
SignInManager<CustomIdentityUser> signInManager,
LinkGenerator linkGenerator,
IHttpContextAccessor accessor,
IMediaService mediaService,
RoleManager<CustomIdentityRole> roleManager,
IUserRepository userRepository)
{
_userManager = userManager;
_signInManager = signInManager;
_linkGenerator = linkGenerator;
_accessor = accessor;
_mediaService = mediaService;
_roleManager = roleManager;
_userRepository = userRepository;
}
and the other called MediaService
:
public class MediaService : IMediaService
{
private readonly IMediaRepository _mediaRepository;
private readonly IAccountService _accountService;
private readonly IHttpContextAccessor _accessor;
public MediaService(
IMediaRepository mediaRepository,
IAccountService accountService,
IHttpContextAccessor accessor)
{
_mediaRepository = mediaRepository;
_accountService = accountService;
_accessor = accessor;
}
}
I injected these two into the IoC layer to use:
services.AddScoped<IMediaService, MediaService>();
services.AddScoped<IAccountService, AccountService>();
But I get an error at runtime:
Error while validating the service descriptor 'ServiceType: Aroma_Shop.Application.Interfaces.IMediaService Lifetime: Scoped ImplementationType: Aroma_Shop.Application.Services.MediaService': A circular dependency was detected for the service of type 'Aroma_Shop.Application.Interfaces.IMediaService'.
Aroma_Shop.Application.Interfaces.IMediaService(Aroma_Shop.Application.Services.MediaService) -> Aroma_Shop.Application.Interfaces.IAccountService(Aroma_Shop.Application.Services.AccountService) -> Aroma_Shop.Application.Interfaces.IMediaService"}
I understand where the problem is, if you look at the two classes AccountService
and MediaService
, you will notice that within each of these two classes, I requested to inject the other class. In AccountService
I requested from service to inject MediaService
, and in MediaService
I requested from service to inject AccountService
The service descriptor can't inject these two classes, because when it wants to inject AccountService
, it needs a MediaService
to generate an object from it, and when it wants to inject MediaService
, it needs AccountService
to generate an object from it.
If possible, please help me solve this problem. Thanks a lot.
Upvotes: 1
Views: 1334
Reputation: 12460
Put simply, you cannot have two classes requiring DI that injects each other, it's a little bit of a chicken and egg scenario.
As some other answers have mentioned, you can use a ServiceLocator pattern, but this is not advisable and is actually just a bandaid that hides the real issue.
The only way to solve this is to remove one of the dependencies. Without seeing what methods each are calling, I think it makes more sense to have AccountService dependant on the MediaService.
So with that said, whatever MediaService is calling inside AccountService, I would create a new service that satisfies that for both services. Let's say MediaService calls the AccountService to find the CurrentLoggedInAccount. I would create an ICurrentAccountService, and move the relevant method from the AccountService to this service. Then have AccountService and MediaService depend on this service.
No matter what, you have to refactor your code. It cannot be solved with a one line fix.
More info : https://dotnetcoretutorials.com/2020/09/14/dealing-with-circular-dependency-injection-references/
Upvotes: 0
Reputation: 141845
You can also register factory to resolve the dependency. Some containers provide build in Func
"factories" for registered services but with Microsoft's one you can do as simple as:
services.AddScoped<Func<IMediaService>>(c => c.GetRequiredService<IMediaService>);
And resolve Func<IMediaService>
instead of IMediaService
in AccountService
:
public AccountService(... Func<IMediaService> mediaServiceFactory,...)
{
...
_mediaService = mediaServiceFactory(); (or in place where it is called)
...
}
Also note that if you have some recursive call path you are just deferring it till the runtime, so in case of infinite recursion you end up with StackOverflow exception, so maybe it is worth of effort to some move parts of the reused functionality to another class to prevent such circular dependencies.
Upvotes: 0
Reputation: 89006
Or if they are so interdependent, you can provide a single implementation for both services, eg
AccountAndMediaService : IAccountService, IMediaService
Upvotes: 1