.NET核心异常:检测到类型的服务存在循环依赖关系



最近我问了一个关于软件架构的问题

服务是否应该直接调用另一个服务或存储库?

回答完这个问题后,我重新组织并重构了我的应用程序。简单地说,我的服务是相互调用的,StudentService需要ClassService(例如获得平均班级分数(,ClassService需要StudentService(将学生分配到该班级(。分类(简化(如下所示:

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;
}
}

服务在.NET Core 中的DI容器中注册

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

在解析期间

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

我得到一个异常检测到类型为的服务的循环依赖项。。。

我理解这里的实现问题,但我不知道如何解决这里的架构问题。

你能提供一些建议吗?

编辑:好的,我有更现实的案例,例如员工、服务和公司。我们有带有抽象通用CRUD存储库的存储库层。然后我们派生了类:EmployeesRepository ServicesDepository和CompaniesDepository。EmployeesRepository实现方法:GetTopEmployesOfTheMonthServicesDepository实现方法:GetTopServicesForEmployeesCompaniesDepository实现方法:GetCompaniesWithTopIncome

在上面的层(我们称之为业务层(上,我们有相同的结构:抽象的通用CRUD帮助程序,例如检查用户权限并从CRUD存储库调用方法。然后我们派生了EmployesHelper、ServicesHerlper和CompaniesHelper。所有这些都检查用户权限,并从适当的存储库调用方法(EmployeesRepository中的EmployesHelper等(。此外,在这个层上,我们有创建更"复杂"对象的方法,这些对象由许多实体组成。例如,CompaniesHelper有一种方法来显示服务销售最多的前五家公司。数据将显示在一个屏幕上,因此它应该由一个API请求生成并作为JSON返回。CompaniesHelper方法中的ShowCompaniesWithServices调用CompaniesHelp方法和EmployeesHelper方法。在第二方面,我们有EmployeesHelper,它实现了返回具有当月最高员工的复杂对象的方法,他们的顶级服务和他们工作的公司,所以它需要Comapnies Helper。

如何解决这种循环依赖关系?有什么设计模式可以解决这个问题吗?

有两种方法:

  1. 编写代码,这样就不需要从StudentService调用ClassService(或从ClassService调用StudentService(

  2. 创建第三类:

    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
    }
    

但是,在大多数情况下,需要正确编写StudentService和ClassService(方法1(

我不完全确定我处理这件事的方式是否非常干净,但它对我来说很好。当我遇到循环依赖问题时,我从依赖注入中删除了该服务,并简单地将该服务作为参数传递给需要它的函数。

所以当你调用这个方法时,它看起来是这样的:

// Inside the Student service
var result = _classService.GetClassStudents(classId, this)

可能不适用于所有人,但就我而言,我的设置非常简单,所以我没有深入研究。

希望这能有所帮助。

所以,我不认为服务应该注入其他服务。

我认为每个服务都应该能够独立,只提供它负责的数据。如果IStudentService的使用者需要来自两个来源的数据,那么它也可以是IClassService的使用者。

我不确定这是否回答了这个问题,但我有一个服务包装器,它将IOC的所有接口包装成一个接口。

在每个单独的服务中,我将通过个人所需的服务,日志,存储库,电子邮件发件人等

例如,EmailSender依赖于IRepository。

所以IServiceWrapper是我的父接口,这防止了循环依赖,我只为我的网络核心控制器使用服务包装器,并为服务使用单独的接口。

我使用了另一种解决方法:Lazy<T>

public class StudentService : IStudentService
{
private readonly IServiceProvider _serviceProvider;
protected Lazy<IClassService> _lazyClassService;
public StudentService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_lazyClassService = new Lazy<IClassService>(() =>
_serviceProvider.GetRequiredService<IClassService>());
}
public SomeInfo GetSomeClassInfo(string studentId)
{
return _lazyClassService.Value.GetFooBar(studentId);
}
}

它可能不是那么干净,也不是每个人的解决方案,但在我相对简单的设置中运行良好。请注意,您不需要其他类ClassService中的Lazy<T>

相关内容

  • 没有找到相关文章

最新更新