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

.NET依赖注入中动态选择实现的最优方案咨询

.NET依赖注入中条件选择接口实现的最优方案探讨

需求背景

需要在.NET依赖注入容器中注册IExampleService接口的两个不同实现(ExampleAService和ExampleBService),并能在特定时机根据条件动态选择实例化对应的实现类。已尝试工厂模式和工厂委托,希望找到更优的实现方式。

示例代码定义

IExampleService 接口与枚举

internal interface IExampleService
{
    public ExampleMode Mode { get; }
    void Show();
}

internal enum ExampleMode
{
    ExampleA,
    ExampleB
}

ExampleAService 实现

public ExampleMode Mode => ExampleMode.ExampleA;
public ExampleAService()
{
    Console.WriteLine("Creating Example A");
}

public void Dispose()
{
    Console.WriteLine("Disposing Example A");
}

public void Show()
{
    Console.WriteLine("Example A Show");
}

ExampleBService 实现

public ExampleMode Mode => ExampleMode.ExampleB;
public ExampleBService()
{
    Console.WriteLine("Creating Example B");
}
public void Dispose()
{
    Console.WriteLine("Disposing Example B");
}

public void Show()
{
    Console.WriteLine("Example B Show");
}

已尝试的实现方式及问题

工厂模式的两种实现

方式一:注入IEnumerable<IExampleService>

通过DI容器注入所有实现的集合,由容器管理生命周期,但会导致所有实现类被提前实例化,带来不必要的性能开销:

internal class ExampleServiceFactory : IExampleServiceFactory
{
    private readonly IEnumerable<IExampleService> exampleServices;

    public ExampleServiceFactory(IEnumerable<IExampleService> exampleServices)
    {
        this.exampleServices = exampleServices;
    }

    public IExampleService GetExampleService(ExampleMode mode)
    {
        // 使用DI注入的服务集合
        return exampleServices.FirstOrDefault(x => x.Mode == mode);
    }
}

方式二:手动实例化服务

手动创建所需服务实例,但需要自行管理对象释放,且无法利用DI容器的生命周期管理:

internal class ExampleServiceFactory : IExampleServiceFactory
{
    public IExampleService GetExampleService(ExampleMode mode)
    {
        // 手动实例化服务
        return mode switch
        {
            ExampleMode.ExampleA => new ExampleAService(),
            ExampleMode.ExampleB => new ExampleBService(),
            _ => throw new NotSupportedException(),
        };
    }
}

需避免的反模式:注入IServiceProvider

曾考虑在工厂中注入IServiceProvider直接解析服务,但这属于服务定位器反模式,会混合控制反转策略,不推荐:

另一种需要避免的服务定位器变体是注入在运行时解析依赖的工厂。这两种做法都会混合控制反转策略。

当前使用的实现方式:工厂委托+键注册

目前通过键注册服务,结合工厂委托在解析时根据条件选择实例,同时保留DI容器的生命周期管理,最终注入包装类供页面、控制器等使用:

builder.Services.AddKeyedSingleton<IExampleService,ExampleAService>("ExampleA");
builder.Services.AddKeyedTransient<IExampleService,ExampleBService>("ExampleB");
builder.Services.AddTransient<IExampleServiceFactory,ExampleServiceFactory>();

builder.Services.AddTransient<ConfigurationProvider>();
//var exampleMode = ConfigurationManager.AppSettings["Device"];

builder.Services.AddTransient<IGenericExampleService, GenericExampleService>((ctx) =>
{
    var configProvider = ctx.GetRequiredService<ConfigurationProvider>();
    var exampleMode = configProvider.GetExampleMode();
    var service = exampleMode switch
    {
        ExampleMode.ExampleA => ctx.GetRequiredKeyedService<IExampleService>("ExampleA"),
        ExampleMode.ExampleB => ctx.GetRequiredKeyedService<IExampleService>("ExampleB"),
        _ => throw new InvalidOperationException()
    };
    return new GenericExampleService(service);
});

内容的提问来源于stack exchange,提问作者O.Emad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 16:33:15