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

在.NET 7中如何注入委托/字典到工厂实现依赖动态解析?

问题描述

我正在开发一个.NET 7的C#解决方案,结构如下:

基础接口定义(类库中)

public interface IFoo<T1, T2> 
    where T1 : Contract1 where T2 : Contract2
{
    Task<ResponseContract> Function1(
        T1 T1Object, 
        T2 T2Object);
}

具体实现(其他类库中)

public class Bar1Contract1 : Contract1{}
public class Bar1Contract2 : Contract2{}
public interface IBar1 : IFoo<Bar1Contract1, Bar1Contract2>{}
public class Bar1 : IBar1{
    public async Task<ResponseContract> Function1(
                Bar1Contract1 T1Object, 
                Bar1Contract2 T2Object){
        //Do something and return ResponseContract
    }
}

public class Bar2Contract1 : Contract1{}
public class Bar2Contract2 : Contract2{}
public interface IBar2 : IFoo<Bar2Contract1, Bar2Contract2>{}
public class Bar2 : IBar2{
    public async Task<ResponseContract> Function1(
                Bar2Contract1 T1Object, 
                Bar2Contract2 T2Object){
        //Do something and return ResponseContract
    }
}

工厂与服务类

public class FooFactory{
    private readonly Func<string, IFoo<Contract1, Contract2>> _fooFactory;

    public FooFactory(Func<string, IFoo<Contract1, Contract2>> fooFactory)
    {
        this._fooFactory = fooFactory;
    }

    public IFoo<Contract1, Contract2> CreateFoo(string input)
    {
        return _fooFactory(input);
    }
}

public interface IFooService{
    Task<ServiceContract1> ServiceFunction1(string input, Contract1 contract1, Contract2 contract2);
}

public class FooService{
    private FooFactory _fooFactory;
    public FooService(FooFactory fooFactory){
            this._fooFactory = fooFactory;
    }
    public async Task<ServiceContract1> ServiceFunction1(string input, Contract1 contract1, Contract2 contract2){
            //do something
            var foo = this._fooFactory.CreateFoo(input);
            var response = await foo.Function1(contract1, contract2);
            // do something else and return serviceContract1
    }
}

依赖注册(Program.cs)

Builder.Services.AddScoped<IBar1, Bar1>();
Builder.Services.AddScoped<IBar2, Bar2>();
Builder.Services.AddSingleton<FooFactory>();
Builder.Services.AddScoped<IFooService, FooService>();

控制器代码

[Route("api/[controller]")]
[ApiController]
public class FooController : ControllerBase
{
    IFooService _fooService;
    public FooController(IFooService fooService) { 
        this._fooService = fooService;
    }
    public async Task<ActionResult> Route1(ViewModel model){
        var response = await this._fooService.ServiceFunction1(model.input, model.Contract1, model.Contract2);
        return Ok(response);
    }
}

当前遇到的问题:无法创建能根据输入字符串解析IFoo的委托函数并注入到工厂中。想知道有没有其他方法根据用户提供的字符串选择对应的实现?也考虑过注入输入与服务实现的字典到工厂,但不清楚如何在Program.cs中注册。


解决方案

方法1:字典映射+IServiceProvider注入

修改FooFactory,通过注入服务提供者和预设的类型映射字典来实现实例创建:

public class FooFactory{
    private readonly IServiceProvider _serviceProvider;
    private readonly Dictionary<string, Type> _fooTypeMap;

    public FooFactory(IServiceProvider serviceProvider, Dictionary<string, Type> fooTypeMap)
    {
        _serviceProvider = serviceProvider;
        _fooTypeMap = fooTypeMap;
    }

    public IFoo<Contract1, Contract2> CreateFoo(string input)
    {
        if(_fooTypeMap.TryGetValue(input, out var fooType))
        {
            return (IFoo<Contract1, Contract2>)_serviceProvider.GetRequiredService(fooType);
        }
        throw new ArgumentException($"未找到对应输入[{input}]的Foo实现");
    }
}

在Program.cs中注册字典和工厂:

// 定义输入字符串与服务接口的映射
var fooTypeMap = new Dictionary<string, Type>
{
    ["bar1"] = typeof(IBar1),
    ["bar2"] = typeof(IBar2)
};
Builder.Services.AddSingleton(fooTypeMap);

// 保持原有服务注册
Builder.Services.AddScoped<IBar1, Bar1>();
Builder.Services.AddScoped<IBar2, Bar2>();
Builder.Services.AddSingleton<FooFactory>();
Builder.Services.AddScoped<IFooService, FooService>();

方法2:硬编码映射(适合简单场景)

如果实现类数量不多,可以直接在工厂中写死映射逻辑,仅注入IServiceProvider:

public class FooFactory{
    private readonly IServiceProvider _serviceProvider;

    public FooFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IFoo<Contract1, Contract2> CreateFoo(string input)
    {
        return input switch
        {
            "bar1" => _serviceProvider.GetRequiredService<IBar1>(),
            "bar2" => _serviceProvider.GetRequiredService<IBar2>(),
            _ => throw new ArgumentException($"不支持的输入值:{input}")
        };
    }
}

注册时无需额外字典,保持原有配置即可:

Builder.Services.AddScoped<IBar1, Bar1>();
Builder.Services.AddScoped<IBar2, Bar2>();
Builder.Services.AddSingleton<FooFactory>();
Builder.Services.AddScoped<IFooService, FooService>();

方法3:修复泛型协变问题(关键注意点)

注意:当前IBar1实现的是IFoo<Bar1Contract1, Bar1Contract2>,直接转换为IFoo<Contract1, Contract2>会存在泛型不变性导致的转换错误。需要修改基础接口为协变:

// 添加out关键字将接口声明为协变
public interface IFoo<out T1, out T2> 
    where T1 : Contract1 where T2 : Contract2
{
    Task<ResponseContract> Function1(
        Contract1 T1Object, 
        Contract2 T2Object);
}

如果无法修改接口,在调用Function1时需要手动将基类参数转换为对应子类类型,否则会触发运行时类型错误:

public async Task<ServiceContract1> ServiceFunction1(string input, Contract1 contract1, Contract2 contract2){
    var foo = this._fooFactory.CreateFoo(input);
    ResponseContract response;
    
    switch(input)
    {
        case "bar1":
            response = await ((IBar1)foo).Function1((Bar1Contract1)contract1, (Bar1Contract2)contract2);
            break;
        case "bar2":
            response = await ((IBar2)foo).Function1((Bar2Contract1)contract1, (Bar2Contract2)contract2);
            break;
        default:
            throw new ArgumentException("无效输入");
    }
    
    // 后续逻辑处理
}

内容的提问来源于stack exchange,提问作者user1439090

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:54:59