C# 如何解决服务层内部的循环依赖关系
我知道其他人已经有了同样的问题,但我找不到任何统计解决方案,所以我在这里询问其他想法 我的业务逻辑包含在服务层中,如下所示:C# 如何解决服务层内部的循环依赖关系,c#,asp.net,asp.net-core,dependency-injection,C#,Asp.net,Asp.net Core,Dependency Injection,我知道其他人已经有了同样的问题,但我找不到任何统计解决方案,所以我在这里询问其他想法 我的业务逻辑包含在服务层中,如下所示: public class RoomService : IRoomService { private readonly IRoomRepository _roomRepository; private readonly ICourseService _courseService; public RoomService(IRoomRepository
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
}
}
问题:现在我的控制器需要了解服务方法的实现细节:DeleteRoomAsync
需要一个ICourseService
对象来完成它的工作。
我认为这不是很清楚,因为DeleteRoomAsync
的要求将来可能会改变,但方法签名不应该改变
您能想出其他更干净的解决方案吗?在提供的示例中,我会重新考虑在这种情况下您是否真的存在服务间依赖关系:
- 在
实施中,您需要RoomService
中包含的逻辑,还是只需要某些课程的信息 我会说是后一种,因此您真正的依赖关系可能是ICourseService
使用方法ICourseRepository
ICourseRepository.FindByRoom(Room)
- 在
实施中,您需要包含在课程服务
中的逻辑,还是只需要现有房间 在这种情况下,IRoomService
就足够了IRoomRepository
然而,这并不总是那么容易,有时您确实需要在服务层中实现逻辑(验证等)。在这些场景中,尝试将该行为提取到共享类中,而不是复制该行为或创建循环依赖关系可能更可取。当然,最好的解决方案是避免循环依赖关系,但如果您确实陷入困境,您可以使用属性注入和(如果您不使用Autofac,也可以使用其等效工具) 例如,如果您有一个相互依赖的
FooService
类和一个BarService
类,则可以执行以下操作:
public static IContainer CompositionRoot()
{
var foo = new FooService();
var bar = new BarService();
foo.Bar = bar;
bar.Foo = foo;
var builder = new ContainerBuilder();
builder.RegisterInstance<IFooService>( foo );
builder.RegisterInstance<IBarService>( bar );
builder.RegisterType<Application>().SingleInstance();
return builder.Build();
}
公共静态IContainer CompositionRoot()
{
var foo=new FooService();
var bar=新的BarService();
foo.Bar=Bar;
bar.Foo=Foo;
var builder=new ContainerBuilder();
建造商注册处(foo);
建造商注册状态(bar);
工作示例的builder.RegisterType。如果您的框架支持它,您可以将注入的依赖项作为延迟解析并允许循环依赖项的惰性提供
下面是这些服务类的外观:
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);
}
}
}
class-FooService:ifoooservice
{
受保护的懒人酒吧;
公共餐饮服务(懒人酒吧)
{
_巴=巴;
}
公共无效DoSomething(布尔呼叫其他服务)
{
Console.WriteLine(“你好,世界,我是福。”);
如果(呼叫其他服务)
{
_巴值DoSomethingElse(假);
}
}
}
BarService类:IBarService
{
保护懒人(u foo);;
公共酒吧服务(懒福)
{
_foo=foo;
}
公共无效DoSomethingElse(bool callOtherService)
{
控制台。WriteLine(“你好,世界,我是酒吧。”);
如果(呼叫其他服务)
{
_foo.Value.DoSomething(假);
}
}
}
注册它们的代码不需要修改(至少不需要使用Autofac):
公共静态IContainer CompositionRoot()
{
var builder=new ContainerBuilder();
builder.RegisterType().As().SingleInstance();
builder.RegisterType().As().SingleInstance();
builder.RegisterType
如果您的框架不支持这样的惰性注入,那么您可能可以使用工厂(或任何其他延迟解析的模式)执行完全相同的操作
另请参阅帮助我提出此解决方案的原因。这是一个有趣的想法。但我认为,当我有许多范围服务时,这些服务都必须像您所示手动实例化,因此无法很好地扩展。顺便说一句,我正在使用ASP.Net Core中包含的ServiceProvider
实现,但我明白您在这里的意思。没关系。在做了一点研究之后,我分别给出了一个更好的答案。正如我在问题中所写的那样,有一些可能的解决方法,如您在本示例场景中所描述的。但我正在寻找一种更通用的解决方案,可以在其他服务中重用服务方法,因为这通常是避免cod的一种非常好的方法重复和保持应用程序逻辑很好地分离。唯一的问题是,我必须解决依赖项,而不是每个服务,而是每个方法,以避免循环依赖项错误。但这也有上述缺点。我建议您检查当前的循环设计气味。而不正确检查代码如何使用对于这些依赖项,无法提供适当的解决方案。您最终只能使用标准的、经过尝试和测试的解决方案,这些解决方案只能解决症状,而不能解决根本原因或气味。根据我的经验,尝试避免循环设计会导致更多的代码气味。我尝试添加一个额外的服务层来解决这种情况但是一旦一些服务方法变得更复杂,它就会崩溃,你又回到了起点。这就是为什么这个问题不是要避免循环设计,而是要找到c
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);
}
}
}
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();
}