在.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
相关产品推荐
相关产品推荐

