如何在以Microsoft DI为主容器的.NET 8 API项目中复用Autofac模块注册遗留服务?
如何在以Microsoft DI为主容器的.NET 8 API项目中复用Autofac模块注册遗留服务?
嘿,我刚好碰到过几乎一模一样的场景!你说得没错,现在的AddAutofac确实是用来把Autofac设为主容器的,完全不符合咱们想让Microsoft DI当家的需求,那玩意儿被弃用也在情理之中。
咱们的核心诉求很明确:不换主容器,直接把Autofac模块里的注册逻辑“搬”到Microsoft DI里,不用手动重新写一遍所有遗留服务的注册。下面给你两个可行的思路,优先推荐第一个:
方案一:写个扩展方法自动转换Autofac模块的注册
这个思路是创建一个临时的Autofac ContainerBuilder,加载你的遗留模块,然后把Builder里的所有注册项映射成Microsoft DI能识别的ServiceDescriptor,再添加到IServiceCollection里。
先上扩展方法的代码,你可以直接拷到项目里用:
using Autofac; using Autofac.Builder; using Microsoft.Extensions.DependencyInjection; using System.Reflection; public static class AutofacModuleExtensions { public static IServiceCollection AddAutofacModuleServices(this IServiceCollection services, IModule module) { var tempBuilder = new ContainerBuilder(); // 先调用模块里针对IServiceCollection的配置(如果模块实现了的话) module.ConfigureServices(services); // 加载模块到临时Builder module.Load(tempBuilder); // 遍历所有Autofac注册,转成Microsoft DI的注册 foreach (var registration in tempBuilder.ComponentRegistry.Registrations) { foreach (var service in registration.Services) { if (service is TypedService typedService) { var descriptor = MapToServiceDescriptor(registration, typedService.ServiceType); services.Add(descriptor); } else if (service is IKeyedService keyedService) { // .NET 8+支持键控服务,这里做简单映射 var descriptor = MapToServiceDescriptor(registration, keyedService.ServiceType); switch (descriptor.Lifetime) { case ServiceLifetime.Singleton: services.AddKeyedSingleton(descriptor.ServiceType, keyedService.ServiceKey, descriptor.ImplementationInstance); break; case ServiceLifetime.Scoped: services.AddKeyedScoped(descriptor.ServiceType, keyedService.ServiceKey, provider => descriptor.ImplementationFactory?.Invoke(provider)); break; case ServiceLifetime.Transient: services.AddKeyedTransient(descriptor.ServiceType, keyedService.ServiceKey, provider => descriptor.ImplementationFactory?.Invoke(provider)); break; } } } } return services; } private static ServiceDescriptor MapToServiceDescriptor(IComponentRegistration registration, Type serviceType) { // 先处理生命周期映射 var lifetime = registration.Lifetime switch { CurrentScopeLifetime => ServiceLifetime.Scoped, RootScopeLifetime => ServiceLifetime.Singleton, _ => ServiceLifetime.Transient }; // 处理不同的激活器类型(实例、反射类型、委托工厂) if (registration.Activator is InstanceActivator instanceActivator) { return new ServiceDescriptor(serviceType, instanceActivator.Instance, lifetime); } else if (registration.Activator is ReflectionActivator reflectionActivator) { var implType = reflectionActivator.LimitType; return new ServiceDescriptor(serviceType, implType, lifetime); } else if (registration.Activator is DelegateActivator delegateActivator) { // 适配Autofac的IComponentContext到Microsoft的IServiceProvider var factory = (IServiceProvider sp) => { var contextAdapter = new ServiceProviderToComponentContextAdapter(sp); return delegateActivator.Factory(contextAdapter, null); }; return new ServiceDescriptor(serviceType, factory, lifetime); } throw new NotSupportedException($"暂时不支持这种激活器类型:{registration.Activator.GetType().Name}"); } // 简单的适配器类,把IServiceProvider转成Autofac需要的IComponentContext private class ServiceProviderToComponentContextAdapter : IComponentContext { private readonly IServiceProvider _sp; public ServiceProviderToComponentContextAdapter(IServiceProvider sp) { _sp = sp; } public IComponentRegistry ComponentRegistry => throw new NotImplementedException("这个场景下暂时不需要实现ComponentRegistry"); public object ResolveComponent(ResolveRequest request) { return _sp.GetService(request.Service); } } }
然后在你的Program.cs里这么用就行:
var builder = WebApplication.CreateBuilder(args); // 先加你自己的服务,比如Controllers builder.Services.AddControllers(); // 重点!加载你的遗留Autofac模块 builder.Services.AddAutofacModuleServices(new LegacyModule()); var app = builder.Build(); // 后面的中间件配置照常写 app.MapControllers(); app.Run();
这个方案的注意事项
- 不是所有Autofac的高级特性都能完美映射,比如Autofac独有的装饰器、自定义生命周期范围、复杂的键控服务逻辑,如果你的遗留模块用了这些,可能需要针对特定注册做额外处理。
- 生命周期映射是基本的:Autofac的
SingleInstance对应Microsoft DI的Singleton,InstancePerLifetimeScope对应Scoped,InstancePerDependency对应Transient,大部分场景下够用。 - 如果你的模块实现了
IModule的ConfigureServices方法(就是专门用来配置IServiceCollection的),这个扩展方法也会自动调用,不会漏掉相关配置。
方案二:手动提取模块内的注册逻辑(适合简单模块)
如果你的遗留模块逻辑不复杂,也可以直接打开模块的Load方法,把里面的builder.RegisterXXX代码改成Microsoft DI的services.AddXXX写法。虽然麻烦点,但胜在绝对可控,没有兼容性问题。比如Autofac的builder.RegisterType<LegacyService>().As<ILegacyService>().SingleInstance();,改成services.AddSingleton<ILegacyService, LegacyService>();就行。
不过这个方案显然不符合你“不想重新注册所有遗留依赖”的初衷,所以只作为备选。
备注:内容来源于stack exchange,提问作者rjs52
相关产品推荐
相关产品推荐

