You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 02:17:05