.NET 6中如何按服务类型条件注入依赖?
问题描述
我有一个基于.NET 6的C# gRPC解决方案(不确定gRPC属性是否对此问题有影响),包含两个实现不同gRPC服务逻辑的C#类,它们分别继承自两个不同proto文件中定义的服务基类:
public class MyServiceV2 : My_Service.My_ServiceBase public class MyServiceV3 : My_Service_V3.My_Service_V3Base
这两个服务同时运行并已注册端点。
这两个类的构造函数均以ClassA作为参数,同时存在ClassA的派生类:
public class ClassAV2 : ClassA public class ClassAV3 : ClassA
如何在启动文件中配置依赖注入(DI),使MyServiceV2的构造函数参数ClassA注入ClassAV2实例,MyServiceV3注入ClassAV3实例?
注:服务的构造函数参数不能直接使用具体派生类,因为不同环境有不同行为:生产环境按上述规则注入,非生产环境仅注入基类ClassA。我已能区分生产与非生产环境,仅需了解生产环境下针对特定服务的注入配置方式。
我期望实现的伪代码:
public void ConfigureServices(IServiceCollection services) { if(InProduction()) //已实现此判断 { services.FindService(typeof(MyServiceV2)).AddSingleton<ClassA, ClassAV2>(); services.FindService(typeof(MyServiceV3)).AddSingleton<ClassA, ClassAV3>(); } else { services.AddSingleton<ClassA>(); } }
解决方案
在.NET 6的DI系统中,要实现针对特定服务注入不同派生类实例,可以通过以下几种实用方式实现:
方法1:利用.NET 6+原生键控服务
.NET 6及以上版本支持键控服务,可给不同ClassA派生类打标识,注册gRPC服务时指定注入对应实例:
public void ConfigureServices(IServiceCollection services) { if (InProduction()) { // 注册带标识的ClassA派生类 services.AddKeyedSingleton<ClassA, ClassAV2>("v2"); services.AddKeyedSingleton<ClassA, ClassAV3>("v3"); // 注册MyServiceV2并指定注入对应键的实例 services.AddGrpc() .AddSingletonServiceProviderFactory<MyServiceV2>(sp => new MyServiceV2(sp.GetRequiredKeyedService<ClassA>("v2"))); // 注册MyServiceV3并指定注入对应键的实例 services.AddGrpc() .AddSingletonServiceProviderFactory<MyServiceV3>(sp => new MyServiceV3(sp.GetRequiredKeyedService<ClassA>("v3"))); } else { services.AddSingleton<ClassA>(); services.AddGrpc(); } }
方法2:手动实例化服务并传入构造参数
如果不想用键控服务,可直接在注册gRPC服务时手动创建实例,明确传入对应派生类:
public void ConfigureServices(IServiceCollection services) { if (InProduction()) { // 注册派生类实例 services.AddSingleton<ClassAV2>(); services.AddSingleton<ClassAV3>(); // 手动注册MyServiceV2,传入ClassAV2 services.AddSingleton<MyServiceV2>(sp => new MyServiceV2(sp.GetRequiredService<ClassAV2>())); // 手动注册MyServiceV3,传入ClassAV3 services.AddSingleton<MyServiceV3>(sp => new MyServiceV3(sp.GetRequiredService<ClassAV3>())); services.AddGrpc(); } else { services.AddSingleton<ClassA>(); services.AddGrpc(); } }
方法3:工厂模式封装注入逻辑
如果需要更灵活的环境适配,可创建工厂类统一处理实例创建:
// 工厂类 public class ClassAFactory { private readonly IServiceProvider _sp; private readonly bool _isProduction; public ClassAFactory(IServiceProvider sp) { _sp = sp; _isProduction = InProduction(); // 复用你已有的环境判断逻辑 } public ClassA GetInstance(Type serviceType) { if (!_isProduction) return _sp.GetRequiredService<ClassA>(); return serviceType switch { Type t when t == typeof(MyServiceV2) => _sp.GetRequiredService<ClassAV2>(), Type t when t == typeof(MyServiceV3) => _sp.GetRequiredService<ClassAV3>(), _ => _sp.GetRequiredService<ClassA>() }; } } // 启动配置 public void ConfigureServices(IServiceCollection services) { if (InProduction()) { services.AddSingleton<ClassAV2>(); services.AddSingleton<ClassAV3>(); } services.AddSingleton<ClassA>(); services.AddSingleton<ClassAFactory>(); // 通过工厂注入对应实例 services.AddSingleton<MyServiceV2>(sp => new MyServiceV2(sp.GetRequiredService<ClassAFactory>().GetInstance(typeof(MyServiceV2)))); services.AddSingleton<MyServiceV3>(sp => new MyServiceV3(sp.GetRequiredService<ClassAFactory>().GetInstance(typeof(MyServiceV3)))); services.AddGrpc(); }
注意事项
- gRPC服务默认生命周期为单例,因此
ClassA派生类也注册为单例可避免重复创建实例。 - 上述方法均兼容
MapGrpcService端点注册方式,DI会自动解析对应服务实例。
内容的提问来源于stack exchange,提问作者CodeMonkey
相关产品推荐
相关产品推荐

