【发布时间】:2020-01-17 19:05:33
【问题描述】:
我知道其他人已经遇到过同样的问题,但我找不到任何令人满意的解决方案,所以我在这里寻求其他想法。
我的业务逻辑包含在这样的服务层中:
public class RoomService : IRoomService
{
private readonly IRoomRepository _roomRepository;
private readonly ICourseService _courseService;
public RoomService(IRoomRepository roomRepository, ICourseService courseService)
{
_roomRepository = roomRepository ?? throw new ArgumentNullException(nameof(roomRepository));
_courseService = courseService ?? throw new ArgumentNullException(nameof(courseService));
}
public Task DeleteRoomAsync(string id)
{
// Check if there are any courses for this room (requires ICourseService)
// Delete room
}
}
public class CourseService : ICourseService
{
private readonly ICourseRepository _courseRepository;
private readonly IRoomService _roomService;
public CourseService(ICourseRepository courseRepository, IRoomService roomService)
{
_courseRepository = courseRepository ?? throw new ArgumentNullException(nameof(courseRepository));
_roomService = roomService ?? throw new ArgumentNullException(nameof(roomService));
}
public Task GetAllCoursesInBuilding(string buildingId)
{
// Query all rooms in building (requires IRoomService)
// Return all courses for these rooms
}
}
这只是一个例子。在这种情况下,可能有一些解决方法可以避免服务相互依赖,但我过去遇到过多种其他情况,没有任何干净的解决方法。
如你所见,这两个服务相互依赖,依赖注入会因为循环依赖而失败。
现在我可以想出两种方法来解决这个问题:
解决方案 1
我可以解决需要它们的服务方法内部的服务依赖关系,而不是将服务依赖关系注入服务构造函数:
public class RoomService : IRoomService
{
private readonly IRoomRepository _roomRepository;
private readonly IServiceProvider _serviceProvider;
public RoomService(IRoomRepository roomRepository, IServiceProvider serviceProvider)
{
_roomRepository = roomRepository ?? throw new ArgumentNullException(nameof(roomRepository));
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public Task DeleteRoomAsync(string id)
{
ICourseService courseService = _serviceProvider.GetRequiredService<ICourseService>();
// Check if there are any courses for this room (requires ICourseService)
// Delete room
}
}
问题:这使得单元测试更加困难,因为我需要注入一个模拟的IServiceProvider,它能够将我的ICourseService 解析到类构造函数中。在编写单元测试时也不是很清楚,每个服务方法需要哪些服务,因为这完全依赖于实现。
解决方案 2
服务方法可能要求 ICourseService 作为方法参数从控制器传入:
public Task DeleteRoomAsync(ICourseService courseService, string id)
{
// Check if there are any courses for this room (requires ICourseService)
// Delete room
}
问题:现在我的控制器需要了解服务方法的实现细节:DeleteRoomAsync 需要一个 ICourseService 对象来完成它的工作。
我认为这不是很干净,因为DeleteRoomAsync 的要求将来可能会改变,但方法签名不应该。
您能想出其他更清洁的解决方案吗?
【问题讨论】:
-
我建议你回顾一下当前循环设计的味道。如果不对代码如何使用这些依赖项进行适当的审查,就无法提供适当的解决方案。您最终只会得到经过试验和测试的标准变通办法,这些变通办法只治疗症状,而不是解决根本原因或气味。
-
根据我的经验,试图避免循环设计会导致更多的代码异味。过去我尝试添加一个额外的服务层来解决这种情况。但是一旦一些服务方法变得更加复杂,这就会中断,你又回到了起点。这就是为什么这个问题不是要避免循环设计,而是要找到处理它的干净方法。
标签: c# asp.net asp.net-core dependency-injection