Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core Exception: A circular dependency was detected for the service of type

Recently I asked a question about software architecture

Should service call another service or repository directly?

After that answer, I reorganized and refactored my application. In a simple way, my services call each other, StudentService requires ClassService (e.g. to get average class score) and ClassService requires StudentService (to get students assigned to the class). The classes (simplified) are shown bellow:

public class StudentService : IStudentService
{
    protected readonly IClassService ClassService;
    public StudentService(IClassService classService)
    {
        ClassService = classService;
    }
}

public class ClassService : IClassService
{
    protected readonly IStudentService StudentService;
    public ClassService(IStudentService studentService)
    {
        StudentService = studentService;
    }
}

Services are registered in DI container in .NET Core

services.AddTransient<IStudentService, StudentService>();
services.AddTransient<IClassService, ClassService>();

During resolve

var studentService = app.ApplicationServices.GetService<IStudentService>();

I get an Exception A circular dependency was detected for the service of type...

I understand the implementation problem here, but I don't know how to fix the architecture problem here.

Could you provide some suggestion?

Edit: Ok, I have more realistic case, e.g. Employees, Services and Companies. We have Repository Layer with abstract generic CRUD repository. Then we have derived classes: EmployeesRepository ServicesRepository and CompaniesRepository. EmployeesRepository implements methods: GetTopEmployeesOfTheMonth ServicesRepository implements methods: GetTopServicesForEmployees CompaniesRepository implements methods: GetCompaniesWithTopIncome

On layer above, (let's call it Business Layer) we have the same structure: abstract generic CRUD Helper, that e.g. checks user privileges and calls methods from CRUD repository. Then we have derived EmployeesHelper, ServicesHerlper and CompaniesHelper. All of them checks user privileges and calls methods from proper repository (EmployeesHelper from EmployeesRepository etc). Moreover, on this layer we have methods for creating more "complex" objects - the objects that are composed from many entities. For example, CompaniesHelper has method to show top five companies with most sold services. The data will be shown on one screen so it should be generated by one API request and returned as JSON. The ShowCompaniesWithServices from CompaniesHelper method calls CompaniesHelper methods and EmployeesHelper methods. On the second side, we have EmployeesHelper, that implements method to return complex object with top employees of the month, their top services and companies that they work, so it needs Comapnies Helper.

How to solve this circular dependency? Is there any Design Pattern to resolve it?

like image 867
Piotr Trojan Avatar asked Dec 04 '18 21:12

Piotr Trojan


2 Answers

You have two ways:

  1. Write code such that it isn't needed to call ClassService from StudentService (or StudentService from ClassService)

  2. Create third class:

    public class StudentService: IStudentService
    {
        private readonly IClassSharedSerive _classSharedService;
        ...
    }
    
    public class ClassService: IClassService
    {
        private readonly IClassSharedSerive _classSharedService;
        private readonly IStudentService _studentService;
        ...
    }
    
    public class ClassSharedService: IClassSharesService
    {
        ... without references to IClassService and IStudentService
    }
    

But, in most cases it is needed to write correctly StudentService and ClassService (way 1)

like image 95
Roma Ruzich Avatar answered Oct 21 '22 00:10

Roma Ruzich


So, I don't believe that services should have other services injected.

I think that each service should be able to stand on its own and provide just the data that it is responsible for. The consumer of the IStudentService can also be a consumer of the IClassService if it needs data from both sources.

like image 37
Gary Stewart Avatar answered Oct 20 '22 22:10

Gary Stewart