Mar*_*ann 7 c# asp.net dependency-injection asp.net-core
我知道其他人已经遇到了同样的问题,但我找不到任何令人满意的解决方案,所以我在这里询问其他想法。
我的业务逻辑包含在这样的服务层中:
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
}
}
Run Code Online (Sandbox Code Playgroud)
这只是一个例子。在这种情况下,可能有解决方法可以避免服务相互依赖,但我过去遇到过多种其他情况,没有任何干净的解决方法。
正如您所看到的,这两个服务相互依赖,并且依赖注入将由于循环依赖而失败。
现在我可以想象两种方法来解决这个问题:
我可以解决需要它们的服务方法内部的服务依赖关系,而不是将服务依赖关系注入到服务构造函数中:
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
}
}
Run Code Online (Sandbox Code Playgroud)
问题:这使得单元测试变得更加困难,因为我需要注入一个IServiceProvider能够将 my 解析ICourseService到类构造函数中的模拟。此外,在编写单元测试时,每个服务方法需要哪些服务也不是很清楚,因为这完全依赖于实现。
服务方法可能要求ICourseService从控制器作为方法参数传入:
public Task DeleteRoomAsync(ICourseService courseService, string id)
{
// Check if there are any courses for this room (requires ICourseService)
// Delete room
}
Run Code Online (Sandbox Code Playgroud)
问题:现在我的控制器需要了解服务方法的实现细节:DeleteRoomAsync需要一个ICourseService对象来完成它的工作。
我认为这不是很干净,因为未来的要求DeleteRoomAsync可能会改变,但方法签名不应该改变。
您能想到任何替代的、更清洁的解决方案吗?
如果您的框架支持它,您可以提供注入的依赖项作为Lazy<T>延迟解析并允许您具有循环依赖项。
这些服务类可能如下所示:
class FooService : IFooService
{
protected Lazy<IBarService> _bar;
public FooService(Lazy<IBarService> bar)
{
_bar = bar;
}
public void DoSomething(bool callOtherService)
{
Console.WriteLine("Hello world. I am Foo.");
if (callOtherService)
{
_bar.Value.DoSomethingElse(false);
}
}
}
class BarService : IBarService
{
protected Lazy<IFooService> _foo;
public BarService(Lazy<IFooService> foo)
{
_foo = foo;
}
public void DoSomethingElse(bool callOtherService)
{
Console.WriteLine("Hello world. I am Bar.");
if (callOtherService)
{
_foo.Value.DoSomething(false);
}
}
}
Run Code Online (Sandbox Code Playgroud)
注册它们的代码不需要修改(至少使用 Autofac 不需要修改):
public static IContainer CompositionRoot()
{
var builder = new ContainerBuilder();
builder.RegisterType<FooService>().As<IFooService>().SingleInstance();
builder.RegisterType<BarService>().As<IBarService>().SingleInstance();
builder.RegisterType<Application>().SingleInstance();
return builder.Build();
}
Run Code Online (Sandbox Code Playgroud)
请参阅DotNetFiddle上的工作示例。
如果您的框架不支持这样的延迟注入,您可能可以使用工厂(或任何其他延迟解析的模式)执行完全相同的操作。
另请参阅这个答案,它帮助我提出了这个解决方案。
| 归档时间: |
|
| 查看次数: |
8810 次 |
| 最近记录: |