如何让Worker类同时兼容Autofac与Microsoft依赖注入框架
统一Worker类版本的三种解决方案
你可以通过以下几种方式实现只维护一个Worker类,同时兼容Autofac和Microsoft.Extensions.DependencyInjection(MS DI)的键控依赖注入:
方案1:条件编译切换特性
如果两个项目是独立编译的,可以给不同项目定义专属的编译符号,在Worker类的构造函数中根据符号切换特性:
- 给Autofac项目添加编译符号
AUTOFAC_DI,给MS DI项目添加MS_DI - 修改Worker构造函数代码:
public class Worker : BackgroundService { // ... public Worker( #if AUTOFAC_DI [KeyFilter(ChannelKeys.Name1)] #elif MS_DI [FromKeyedServices(ChannelKeys.Name1)] #endif Channel<Dictionary<string, object>> dataChannel1, #if AUTOFAC_DI [KeyFilter(ChannelKeys.Name2)] #elif MS_DI [FromKeyedServices(ChannelKeys.Name2)] #endif Channel<Dictionary<string, object>> dataChannel2, // ...其他参数 ) { // 构造函数逻辑 } // ...BackgroundService的ExecuteAsync实现 }
这种方式简单直接,不用额外扩展逻辑,适合两个项目代码独立的场景。
方案2:自定义兼容特性并扩展DI容器
自己定义一个通用的键控注入特性,然后分别在Autofac和MS DI中配置该特性的解析规则,让两个容器都能识别这个自定义特性:
- 定义通用特性:
[AttributeUsage(AttributeTargets.Parameter)] public class KeyedInjectAttribute : Attribute { public object Key { get; } public KeyedInjectAttribute(object key) { Key = key; } }
- 修改Worker构造函数,使用自定义特性:
public class Worker : BackgroundService { // ... public Worker( [KeyedInject(ChannelKeys.Name1)] Channel<Dictionary<string, object>> dataChannel1, [KeyedInject(ChannelKeys.Name2)] Channel<Dictionary<string, object>> dataChannel2, // ...其他参数 ) { // 构造函数逻辑 } // ... }
- 给Autofac添加特性解析支持:
在Autofac容器构建时,注册参数解析器映射自定义特性的逻辑:
var builder = new ContainerBuilder(); // 注册带键的Channel服务 builder.RegisterType<Channel<Dictionary<string, object>>>() .Keyed<Channel<Dictionary<string, object>>>(ChannelKeys.Name1); builder.RegisterType<Channel<Dictionary<string, object>>>() .Keyed<Channel<Dictionary<string, object>>>(ChannelKeys.Name2); // 注册Worker并处理自定义特性 builder.RegisterType<Worker>() .As<IHostedService>() .WithParameter( (pi, ctx) => pi.GetCustomAttribute<KeyedInjectAttribute>() != null, (pi, ctx) => { var attr = pi.GetCustomAttribute<KeyedInjectAttribute>(); return ctx.ResolveKeyed(attr.Key, pi.ParameterType); });
- 给MS DI添加特性解析支持:
通过工厂方式注册Worker,手动解析带键服务:
var services = new ServiceCollection(); // 注册带键的Channel服务 services.AddKeyedSingleton<Channel<Dictionary<string, object>>>(ChannelKeys.Name1); services.AddKeyedSingleton<Channel<Dictionary<string, object>>>(ChannelKeys.Name2); // 工厂模式注册Worker services.AddHostedService(provider => { var channel1 = provider.GetRequiredKeyedService<Channel<Dictionary<string, object>>>(ChannelKeys.Name1); var channel2 = provider.GetRequiredKeyedService<Channel<Dictionary<string, object>>>(ChannelKeys.Name2); return new Worker(channel1, channel2, ...); });
这种方式不需要修改项目编译配置,Worker类完全通用,适合作为共享类库的场景。
方案3:移除构造函数特性,通过DI注册时指定参数来源
完全去掉构造函数上的特性,在两个DI容器注册Worker时,手动指定构造函数参数的解析方式:
- 修改Worker构造函数,移除所有注入特性:
public class Worker : BackgroundService { // ... public Worker( Channel<Dictionary<string, object>> dataChannel1, Channel<Dictionary<string, object>> dataChannel2, // ...其他参数 ) { // 构造函数逻辑 } // ... }
- Autofac注册时指定参数:
var builder = new ContainerBuilder(); // 注册带键的Channel服务 builder.RegisterType<Channel<Dictionary<string, object>>>() .Keyed<Channel<Dictionary<string, object>>>(ChannelKeys.Name1); builder.RegisterType<Channel<Dictionary<string, object>>>() .Keyed<Channel<Dictionary<string, object>>>(ChannelKeys.Name2); // 注册Worker并指定参数来源 builder.RegisterType<Worker>() .As<IHostedService>() .WithParameter(new ResolvedParameter( (pi, ctx) => pi.Name == "dataChannel1", (pi, ctx) => ctx.ResolveKeyed<Channel<Dictionary<string, object>>>(ChannelKeys.Name1) )) .WithParameter(new ResolvedParameter( (pi, ctx) => pi.Name == "dataChannel2", (pi, ctx) => ctx.ResolveKeyed<Channel<Dictionary<string, object>>>(ChannelKeys.Name2) ));
- MS DI注册时指定参数:
var services = new ServiceCollection(); // 注册带键的Channel服务 services.AddKeyedSingleton<Channel<Dictionary<string, object>>>(ChannelKeys.Name1); services.AddKeyedSingleton<Channel<Dictionary<string, object>>>(ChannelKeys.Name2); // 工厂模式注册Worker services.AddHostedService(provider => { return new Worker( provider.GetRequiredKeyedService<Channel<Dictionary<string, object>>>(ChannelKeys.Name1), provider.GetRequiredKeyedService<Channel<Dictionary<string, object>>>(ChannelKeys.Name2), // ...其他参数 ); });
这种方式让Worker类完全脱离DI框架的特性依赖,通用性最强,但需要在每个项目的DI注册代码中维护参数映射逻辑。
内容的提问来源于stack exchange,提问作者user24500090
相关产品推荐
相关产品推荐

