.NET中如何通过依赖注入解决适配器类的循环依赖问题
问题背景
我维护着数十个负责不同协议实体转换的Adapter类,原本运行正常,但在处理带关联关系的实体转换时遇到了循环依赖问题。示例中:
- Client可能关联Agent,Agent可能关联User,User可能关联Client(业务数据本身无循环)
ClientAdapter、AgentAdapter、UserAdapter因互相调用对方的转换方法,形成了类型层面的循环依赖,导致.NET依赖注入在运行时抛出循环依赖错误。
示例代码
ClientAdapter.cs
public interface IClientAdapter : IModelAdapter<ClientModel, ClientDatabaseModel, ClientResponseModel> { } public class ClientAdapter : IClientAdapter { IAgentAdapter _agentAdapter; public ClientAdapter(IAgentAdapter agentAdapter) { _agentAdapter = agentAdapter; } public ClientResponseModel convertFromModelToResponseModel(ClientModel model) { return new ClientResponseModel() { Id = model.Id, AgentId = model.AgentId, Agent = model.Agent == null ? null : _agentAdapter.convertFromModelToResponseModel(model.Agent), Name = model.Name }; } }
AgentAdapter.cs
public interface IAgentAdapter : IModelAdapter<AgentModel, AgentDatabaseModel, AgentResponseModel> { } public class AgentAdapter : IAgentAdapter { IUserAdapter _userAdapter; public AgentAdapter(IUserAdapter userAdapter) { _userAdapter = userAdapter; } public AgentResponseModel convertFromModelToResponseModel(AgentModel model) { return new AgentResponseModel() { Id = model.Id, UserId = model.UserId, User = model.User == null ? null : _userAdapter.convertFromModelToResponseModel(model.User), Name = model.Name }; } }
UserAdapter.cs
public interface IUserAdapter : IModelAdapter<UserModel, UserDatabaseModel, UserResponseModel> { } public class UserAdapter : IUserAdapter { IClientAdapter _clientAdapter; public UserAdapter(IClientAdapter clientAdapter) { _clientAdapter = clientAdapter; } public UserResponseModel convertFromModelToResponseModel(UserModel model) { return new UserResponseModel() { Id = model.Id, ClientId = model.ClientId, Client = model.UserClient == null ? null : _clientAdapter.convertFromModelToResponseModel(model.Client), Name = model.Name }; } }
当前DI配置
var builder = WebApplication.CreateBuilder(args); // add services to DI container { var services = builder.Services; services.AddScoped<IAgentAdapter, AgentAdapter>(); services.AddScoped<IClientAdapter, ClientAdapter>(); services.AddScoped<IUserAdapter, UserAdapter>(); } var app = builder.Build();
依赖注入层面的解决方案
方案1:使用延迟注入(Lazy)
通过Lazy<T>包装依赖,延迟依赖实例的创建时机,避免DI容器初始化时的循环引用。
修改ClientAdapter示例:
public class ClientAdapter : IClientAdapter { private readonly Lazy<IAgentAdapter> _agentAdapter; // 注入Lazy<IAgentAdapter>而非直接注入IAgentAdapter public ClientAdapter(Lazy<IAgentAdapter> agentAdapter) { _agentAdapter = agentAdapter; } public ClientResponseModel convertFromModelToResponseModel(ClientModel model) { return new ClientResponseModel() { Id = model.Id, AgentId = model.AgentId, Agent = model.Agent == null ? null : // 通过Value属性获取实际实例 _agentAdapter.Value.convertFromModelToResponseModel(model.Agent), Name = model.Name }; } }
同理修改AgentAdapter和UserAdapter的依赖注入为Lazy<IUserAdapter>、Lazy<IClientAdapter>,DI配置无需修改(.NET DI会自动处理Lazy<T>的注入)。
方案2:使用服务定位器(IServiceProvider)
注入IServiceProvider,在需要转换关联实体时再动态获取对应的Adapter,避免构造函数层面的循环依赖。
修改ClientAdapter示例:
public class ClientAdapter : IClientAdapter { private readonly IServiceProvider _serviceProvider; public ClientAdapter(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public ClientResponseModel convertFromModelToResponseModel(ClientModel model) { if (model.Agent == null) { return new ClientResponseModel() { Id = model.Id, AgentId = model.AgentId, Agent = null, Name = model.Name }; } // 动态获取IAgentAdapter实例 var agentAdapter = _serviceProvider.GetRequiredService<IAgentAdapter>(); return new ClientResponseModel() { Id = model.Id, AgentId = model.AgentId, Agent = agentAdapter.convertFromModelToResponseModel(model.Agent), Name = model.Name }; } }
此方案无需修改DI配置,但要注意:服务定位器模式可能降低代码可测试性,建议仅在这类循环依赖场景下使用。
方案3:重构Adapter,引入中间转换服务
抽离关联转换的逻辑到一个统一的IAdapterResolver服务,让所有Adapter依赖这个Resolver而非互相依赖,从根源上消除循环。
步骤1:定义IAdapterResolver
public interface IAdapterResolver { TAdapter GetAdapter<TAdapter>() where TAdapter : class; } public class AdapterResolver : IAdapterResolver { private readonly IServiceProvider _serviceProvider; public AdapterResolver(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public TAdapter GetAdapter<TAdapter>() where TAdapter : class { return _serviceProvider.GetRequiredService<TAdapter>(); } }
步骤2:修改Adapter依赖
以ClientAdapter为例:
public class ClientAdapter : IClientAdapter { private readonly IAdapterResolver _adapterResolver; public ClientAdapter(IAdapterResolver adapterResolver) { _adapterResolver = adapterResolver; } public ClientResponseModel convertFromModelToResponseModel(ClientModel model) { return new ClientResponseModel() { Id = model.Id, AgentId = model.AgentId, Agent = model.Agent == null ? null : _adapterResolver.GetAdapter<IAgentAdapter>() .convertFromModelToResponseModel(model.Agent), Name = model.Name }; } }
步骤3:更新DI配置
var builder = WebApplication.CreateBuilder(args); // add services to DI container { var services = builder.Services; // 注册Resolver services.AddScoped<IAdapterResolver, AdapterResolver>(); services.AddScoped<IAgentAdapter, AgentAdapter>(); services.AddScoped<IClientAdapter, ClientAdapter>(); services.AddScoped<IUserAdapter, UserAdapter>(); } var app = builder.Build();
此方案更符合依赖倒置原则,代码可维护性更高,适合大规模Adapter场景。
方案4:使用属性注入
.NET DI支持属性注入,通过配置允许属性注入,将构造函数的依赖转移到属性上,避免构造函数循环。
步骤1:修改Adapter为属性注入
以ClientAdapter为例:
public class ClientAdapter : IClientAdapter { // 标记为可注入的属性 [Inject] public IAgentAdapter AgentAdapter { get; set; } // 保留无参构造函数 public ClientAdapter() { } public ClientResponseModel convertFromModelToResponseModel(ClientModel model) { return new ClientResponseModel() { Id = model.Id, AgentId = model.AgentId, Agent = model.Agent == null ? null : AgentAdapter.convertFromModelToResponseModel(model.Agent), Name = model.Name }; } }
步骤2:配置DI启用属性注入
var builder = WebApplication.CreateBuilder(args); // add services to DI container { var services = builder.Services; // 配置所有Adapter使用属性注入 services.AddScoped<IClientAdapter, ClientAdapter>(sp => { var adapter = new ClientAdapter(); sp.InjectProperties(adapter); return adapter; }); services.AddScoped<IAgentAdapter, AgentAdapter>(sp => { var adapter = new AgentAdapter(); sp.InjectProperties(adapter); return adapter; }); services.AddScoped<IUserAdapter, UserAdapter>(sp => { var adapter = new UserAdapter(); sp.InjectProperties(adapter); return adapter; }); } var app = builder.Build();
注意:属性注入默认是可选的,需确保属性被正确注入,否则会出现空引用异常,适合对依赖注入灵活性要求较高的场景。
内容的提问来源于stack exchange,提问作者kamii
相关产品推荐
相关产品推荐

