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

如何让Worker类同时兼容Autofac与Microsoft依赖注入框架

统一Worker类版本的三种解决方案

你可以通过以下几种方式实现只维护一个Worker类,同时兼容Autofac和Microsoft.Extensions.DependencyInjection(MS DI)的键控依赖注入:

方案1:条件编译切换特性

如果两个项目是独立编译的,可以给不同项目定义专属的编译符号,在Worker类的构造函数中根据符号切换特性:

  1. 给Autofac项目添加编译符号 AUTOFAC_DI,给MS DI项目添加 MS_DI
  2. 修改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中配置该特性的解析规则,让两个容器都能识别这个自定义特性:

  1. 定义通用特性:
[AttributeUsage(AttributeTargets.Parameter)]
public class KeyedInjectAttribute : Attribute
{
    public object Key { get; }

    public KeyedInjectAttribute(object key)
    {
        Key = key;
    }
}
  1. 修改Worker构造函数,使用自定义特性:
public class Worker : BackgroundService
{
    // ...

    public Worker(
        [KeyedInject(ChannelKeys.Name1)]
        Channel<Dictionary<string, object>> dataChannel1,        
        [KeyedInject(ChannelKeys.Name2)]
        Channel<Dictionary<string, object>> dataChannel2,
        // ...其他参数
    )
    {
        // 构造函数逻辑
    }

    // ...
}
  1. 给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);
           });
  1. 给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时,手动指定构造函数参数的解析方式:

  1. 修改Worker构造函数,移除所有注入特性:
public class Worker : BackgroundService
{
    // ...

    public Worker(
        Channel<Dictionary<string, object>> dataChannel1,        
        Channel<Dictionary<string, object>> dataChannel2,
        // ...其他参数
    )
    {
        // 构造函数逻辑
    }

    // ...
}
  1. 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)
       ));
  1. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 17:45:59