"检测到循环依赖!"——这个简短的错误提示,看似普通,却隐藏着复杂的技术挑战,成为无数程序员开发过程中挥之不去的噩梦。事实上,循环依赖问题本质上是一个设计层面的缺陷。当模块、类或组件之间相互引用形成闭环时,程序往往会出现难以调试的运行时错误,甚至导致系统崩溃。这种问题在大型项目中尤为常见,特别是在涉及多层次架构和复杂业务逻辑的场景下。
今天,我将通过一个真实的工业设备管理系统案例,教你用**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 是.NET Framework提供的延迟初始化工具,它的核心思想是:只有在真正需要时才创建对象实例。
Lazy<IServiceType>
替代直接的IServiceType
依赖.Value
属性时才会创建服务实例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("✅ 维护任务已安排");
}
运行输出:
.Value
时会有创建开销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);
}
想知道这套方案在你的项目中效果如何?遇到了其他循环依赖的奇葩场景?欢迎在评论区分享你的使用经验,或者提出你遇到的技术难题!
觉得这篇文章对你有帮助?请转发给更多需要的同行,让我们一起告别循环依赖的噩梦! 🚀
.Value
时才创建实例,完美破解启动时的循环依赖检测本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!