Autofac命名服务与动态实例化失效问题排查及方案咨询
问题背景
对象依赖链:Client -> Handler -> HandlerSettings
应用需要多个不同配置的Client实例:
// 实例关系 Client1 -> Handler1 -> HandlerSettings1 Client2 -> Handler2 -> HandlerSettings2 ...
使用Autofac命名服务时,直接注入命名服务可正常运行,但结合**动态实例化(Func<T>注入)**时失效,抛出依赖解析异常。
复现代码
类定义
public class Depy1 { } public class Class1 { public Class1(Depy1 depy1) { } } public class Class2 { public Class2(Func<Depy1> depy1) // 动态实例化 { } }
Autofac配置
var named = "bork!"; builder.RegisterType<Depy1>() .Named<Depy1>(named) .InstancePerDependency(); builder.RegisterType<Class1>() .WithParameter(ResolvedParameter.ForNamed<Depy1>(named)) .InstancePerDependency(); builder.RegisterType<Class2>() .WithParameter(ResolvedParameter.ForNamed<Depy1>(named)) .InstancePerDependency();
解析代码
var class1 = container.Resolve<Class1>(); // 正常运行 var class2 = container.Resolve<Class2>(); // 抛出异常!
报错信息
Autofac.Core.DependencyResolutionException
HResult=0x80131500
Message=None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Example.Class2' can be invoked with the available services and parameters:
Cannot resolve parameter 'System.Func1[Example.Depy1] depy1' of constructor 'Void .ctor(System.Func1[Example.Depy1])'.
Source=Autofac
StackTrace:
at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable1 parameters) at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable1 parameters) at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable1 parameters) at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable1 parameters)
at Autofac.Core.Container.ResolveComponent(IComponentRegistration registration, IEnumerable1 parameters) at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable1 parameters, Object& instance)
at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable1 parameters) at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context)
at Example.Program.d__2.MoveNext() in D:\work\client\samples\Example\Program.cs:line 175
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Example.Program.d__0.MoveNext() in D:\work\client\samples\Example\Program.cs:line 34
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Example.Program.(String[] args) This exception was originally thrown at this call stack:
[External Code]
Example.Program.Showcase(Autofac.IContainer, Microsoft.Extensions.Configuration.IConfigurationRoot) in Program.cs
[External Code]
Example.Program.Main(string[]) in Program.cs
[External Code]
原因分析
- Autofac的
ResolvedParameter.ForNamed<T>仅能匹配直接注入的T类型参数,无法识别Func<T>类型的参数。 - Autofac自动提供
Func<T>实例的前提是容器中存在**默认(未命名)**的T注册,或显式注册了Func<T>服务。当仅存在命名的T注册时,容器无法自动生成对应的Func<T>,也不会自动关联命名服务到Func<T>的解析逻辑。
解决方案
方案1:显式注册命名服务对应的Func<T>
手动注册返回命名Depy1的Func<Depy1>,让容器能直接解析该类型:
var named = "bork!"; builder.RegisterType<Depy1>() .Named<Depy1>(named) .InstancePerDependency(); // 显式注册Func<Depy1>,返回指定命名的Depy1实例 builder.Register<Func<Depy1>>(c => { var ctx = c.Resolve<IComponentContext>(); return () => ctx.ResolveNamed<Depy1>(named); }); builder.RegisterType<Class2>() .InstancePerDependency(); // 无需额外参数配置
方案2:通过Lambda注册Class2,手动注入Func<T>
在注册Class2时,直接通过工厂方法创建实例,手动绑定命名服务的动态获取逻辑:
var named = "bork!"; builder.RegisterType<Depy1>() .Named<Depy1>(named) .InstancePerDependency(); builder.Register(c => { var getDepy1 = () => c.ResolveNamed<Depy1>(named); return new Class2(getDepy1); }) .As<Class2>() .InstancePerDependency();
方案3:使用IIndex<TKey, TService>适配多命名实例场景
如果需要管理多个命名的Depy1实例,可通过IIndex<string, Depy1>实现灵活的实例获取:
// 注册多个命名Depy1 builder.RegisterType<Depy1>().Named<Depy1>("bork!"); builder.RegisterType<Depy1>().Named<Depy1>("another!"); // 注册Class2时注入可指定命名的工厂函数 builder.RegisterType<Class2>() .WithParameter((pi, ctx) => pi.ParameterType == typeof(Func<string, Depy1>), (pi, ctx) => (Func<string, Depy1>)(name => ctx.ResolveNamed<Depy1>(name))) .InstancePerDependency(); // 修改Class2构造函数适配 public class Class2 { public Class2(Func<string, Depy1> depy1Factory) { // 使用时传入命名即可获取对应实例 var depy = depy1Factory("bork!"); } }
内容的提问来源于stack exchange,提问作者hIpPy

