最近我问了一个关于软件架构的问题
服务是否应该直接调用另一个服务或存储库?
回答完这个问题后,我重新组织并重构了我的应用程序。简单地说,我的服务是相互调用的,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。
如何解决这种循环依赖关系?有什么设计模式可以解决这个问题吗?
有两种方法:
-
编写代码,这样就不需要从StudentService调用ClassService(或从ClassService调用StudentService(
-
创建第三类:
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>
。