编辑
2025-09-18
C#
00

目录

🎯 循环依赖:开发者的噩梦
💥 典型的循环依赖场景
🚀 Lazy<T>:延迟加载的救星
⭐ 解决方案核心原理
💡 完整实战代码解析
🔧 第一步:创建Lazy扩展方法
🏭 第二步:改造服务类
🏗️ 第三步:车间服务实现
⚙️ 第四步:依赖注入配置
🎯 实际运行效果展示
⚠️ 重要注意事项
🔍 性能考虑
🚨 使用陷阱
🏆 最佳实践总结
📋 实施清单
💡 设计原则
🎯 关键要点回顾

"检测到循环依赖!"——这个简短的错误提示,看似普通,却隐藏着复杂的技术挑战,成为无数程序员开发过程中挥之不去的噩梦。事实上,循环依赖问题本质上是一个设计层面的缺陷。当模块、类或组件之间相互引用形成闭环时,程序往往会出现难以调试的运行时错误,甚至导致系统崩溃。这种问题在大型项目中尤为常见,特别是在涉及多层次架构和复杂业务逻辑的场景下。

今天,我将通过一个真实的工业设备管理系统案例,教你用**Lazy**这个神器,彻底解决循环依赖问题,让你的代码重获新生!

🎯 循环依赖:开发者的噩梦

在复杂的业务系统中,服务之间相互依赖是常见的设计模式。但当这种依赖形成闭环时,就会产生循环依赖问题。

💥 典型的循环依赖场景

以我们的工业设备管理系统为例:

C#
// 设备服务需要检查车间状态 public class EquipmentService : IEquipmentService { private readonly IWorkshopService _workshopService; // 依赖车间服务 private readonly IMaintenanceService _maintenanceService; // 依赖维护服务 public async Task<Equipment> GetEquipmentAsync(int equipmentId) { var equipment = await _equipmentRepository.GetByIdAsync(equipmentId); // 需要检查设备所在车间是否正常运营 var isWorkshopOperational = await _workshopService.IsWorkshopOperationalAsync(equipment.WorkshopId); return equipment; } } // 车间服务需要获取设备信息 public class WorkshopService : IWorkshopService { private readonly IEquipmentService _equipmentService; // 依赖设备服务 public async Task<bool> IsWorkshopOperationalAsync(int workshopId) { // 需要检查车间内的设备运行状况 var equipments = await _equipmentService.GetEquipmentsByWorkshopAsync(workshopId); return equipments.Any(e => e.Status == EquipmentStatus.Running); } }

这就形成了经典的循环依赖:EquipmentService → WorkshopService → EquipmentService

🚀 Lazy:延迟加载的救星

Lazy 是.NET Framework提供的延迟初始化工具,它的核心思想是:只有在真正需要时才创建对象实例

⭐ 解决方案核心原理

  1. 延迟创建:使用Lazy<IServiceType>替代直接的IServiceType依赖
  2. 按需加载:只有访问.Value属性时才会创建服务实例
  3. 破坏循环:避免了启动时的循环依赖检测

💡 完整实战代码解析

🔧 第一步:创建Lazy扩展方法

C#
public static class ServiceCollectionLazyExtensions { public static IServiceCollection AddLazyResolution(this IServiceCollection services) { // 注册Lazy<T>工厂,使依赖注入容器能够自动创建Lazy<T>实例 services.AddTransient(typeof(Lazy<>), typeof(LazyFactory<>)); return services; } private class LazyFactory<T> : Lazy<T> { public LazyFactory(IServiceScopeFactory serviceScopeFactory) : base(() => { // 🔥 关键:创建新的作用域来获取服务实例 using var scope = serviceScopeFactory.CreateScope(); return scope.ServiceProvider.GetRequiredService<T>(); }) { } } }

🏭 第二步:改造服务类

C#
/// <summary> /// 设备服务 - 使用Lazy<T>避免循环依赖 /// </summary> public class EquipmentService : IEquipmentService { private readonly IEquipmentRepository _equipmentRepository; // 🔥 使用Lazy<T>延迟加载,避免循环依赖 private readonly Lazy<IWorkshopService> _workshopService; private readonly Lazy<IMaintenanceService> _maintenanceService; private readonly ILogger<EquipmentService> _logger; public EquipmentService( IEquipmentRepository equipmentRepository, Lazy<IWorkshopService> workshopService, Lazy<IMaintenanceService> maintenanceService, ILogger<EquipmentService> logger) { _equipmentRepository = equipmentRepository; _workshopService = workshopService; _maintenanceService = maintenanceService; _logger = logger; } public async Task<Equipment> GetEquipmentAsync(int equipmentId) { _logger.LogInformation($"获取设备信息: {equipmentId}"); var equipment = await _equipmentRepository.GetByIdAsync(equipmentId); if (equipment == null) return null; // ✅ 只有在真正需要时才会创建WorkshopService实例 var isWorkshopOperational = await _workshopService.Value.IsWorkshopOperationalAsync(equipment.WorkshopId); if (!isWorkshopOperational) { equipment.Status = EquipmentStatus.Stopped; } return equipment; } public async Task<bool> IsEquipmentOperationalAsync(int equipmentId) { var equipment = await _equipmentRepository.GetByIdAsync(equipmentId); if (equipment == null || !equipment.IsActive) return false; // ✅ 只有在真正需要时才会创建MaintenanceService实例 var needsMaintenance = await _maintenanceService.Value.IsMaintenanceRequiredAsync(equipmentId); if (needsMaintenance) { _logger.LogInformation($"设备 {equipmentId} 需要维护,暂时不可操作"); return false; } return equipment.Status == EquipmentStatus.Running; } }

🏗️ 第三步:车间服务实现

C#
/// <summary> /// 车间服务 - 使用Lazy<T>避免循环依赖 /// </summary> public class WorkshopService : IWorkshopService { private readonly IWorkshopRepository _workshopRepository; // 🔥 使用Lazy<T>延迟加载,避免循环依赖 private readonly Lazy<IEquipmentService> _equipmentService; private readonly ILogger<WorkshopService> _logger; public WorkshopService( IWorkshopRepository workshopRepository, Lazy<IEquipmentService> equipmentService, ILogger<WorkshopService> logger) { _workshopRepository = workshopRepository; _equipmentService = equipmentService; _logger = logger; } public async Task<bool> IsWorkshopOperationalAsync(int workshopId) { var workshop = await _workshopRepository.GetByIdAsync(workshopId); if (workshop == null || !workshop.IsOperational) return false; // ✅ 只有在真正需要时才会创建EquipmentService实例 var equipments = await _equipmentService.Value.GetEquipmentsByWorkshopAsync(workshopId); foreach (var equipment in equipments) { var isOperational = await _equipmentService.Value.IsEquipmentOperationalAsync(equipment.Id); if (isOperational) return true; } return false; } }

⚙️ 第四步:依赖注入配置

C#
public static class ServiceConfiguration { public static IServiceCollection AddIndustrialServices(this IServiceCollection services) { // 注册仓储 services.AddScoped<IEquipmentRepository, EquipmentRepository>(); services.AddScoped<IWorkshopRepository, WorkshopRepository>(); services.AddScoped<IMaintenanceRepository, MaintenanceRepository>(); // 🔥 注册服务 - 依赖注入容器会自动处理Lazy<T>的创建 services.AddScoped<IEquipmentService, EquipmentService>(); services.AddScoped<IWorkshopService, WorkshopService>(); services.AddScoped<IMaintenanceService, MaintenanceService>(); // 添加Lazy<T>支持 services.AddLazyResolution(); return services; } /// <summary> /// 验证没有循环依赖 /// </summary> public static void ValidateNoDependencies(IServiceProvider serviceProvider) { try { Console.WriteLine("🔍 正在验证服务依赖关系..."); using var scope = serviceProvider.CreateScope(); var equipmentService = scope.ServiceProvider.GetRequiredService<IEquipmentService>(); var workshopService = scope.ServiceProvider.GetRequiredService<IWorkshopService>(); var maintenanceService = scope.ServiceProvider.GetRequiredService<IMaintenanceService>(); Console.WriteLine("✅ 服务解析成功,没有检测到循环依赖"); } catch (InvalidOperationException ex) when (ex.Message.Contains("circular")) { Console.WriteLine($"🚨 检测到循环依赖: {ex.Message}"); throw; } } }

🎯 实际运行效果展示

让我们看看这个解决方案的实际效果:

C#
public async Task RunDemoAsync() { Console.WriteLine("🏭 工业设备管理系统演示开始"); // 演示1: 获取设备信息(会触发车间状态检查) var equipment = await _equipmentService.GetEquipmentAsync(1); Console.WriteLine($"设备名称: {equipment.Name}"); Console.WriteLine($"设备状态: {equipment.Status}"); // 演示2: 检查车间运营状态(会触发设备状态检查) var isOperational = await _workshopService.IsWorkshopOperationalAsync(1); Console.WriteLine($"车间运营状态: {(isOperational ? "正常运营" : "停止运营")}"); // 演示3: 维护调度(会触发设备信息获取) await _maintenanceService.ScheduleMaintenanceAsync(2, MaintenanceType.Preventive, "预防性维护"); Console.WriteLine("✅ 维护任务已安排"); }

运行输出:

image.png

⚠️ 重要注意事项

🔍 性能考虑

  1. 首次访问成本:首次访问.Value时会有创建开销
  2. 内存占用:Lazy会保持对象引用,注意内存泄漏
  3. 线程安全:Lazy默认是线程安全的

🚨 使用陷阱

C#
// ❌ 错误:在构造函数中访问.Value public EquipmentService(Lazy<IWorkshopService> workshopService) { _workshopService = workshopService; // 🚨 绝对不要在构造函数中这样做! var service = _workshopService.Value; // 会导致循环依赖 } // ✅ 正确:只在需要时访问.Value public async Task<Equipment> GetEquipmentAsync(int equipmentId) { // ✅ 在方法中按需访问 var isOperational = await _workshopService.Value.IsWorkshopOperationalAsync(workshopId); }

🏆 最佳实践总结

📋 实施清单

  1. 识别循环依赖:使用依赖图分析工具
  2. 选择合适的服务:将非核心依赖改为Lazy
  3. 配置扩展方法:添加AddLazyResolution()支持
  4. 测试验证:确保所有路径都能正常工作

💡 设计原则

  • 最小化原则:只对真正需要延迟的依赖使用Lazy
  • 一致性原则:在同一个项目中保持一致的使用模式
  • 文档化原则:明确标注哪些依赖是延迟加载的

想知道这套方案在你的项目中效果如何?遇到了其他循环依赖的奇葩场景?欢迎在评论区分享你的使用经验,或者提出你遇到的技术难题!

觉得这篇文章对你有帮助?请转发给更多需要的同行,让我们一起告别循环依赖的噩梦! 🚀


🎯 关键要点回顾

  1. Lazy延迟加载:只有在访问.Value时才创建实例,完美破解启动时的循环依赖检测
  2. 依赖注入集成:通过自定义扩展方法,让DI容器自动处理Lazy的创建和管理
  3. 实战应用价值:适合解决复杂企业级系统中的服务间循环引用,保持代码架构的清晰性

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!