使用AnyConcreteTypeNotAlreadyRegisteredSource时Autofac嵌套作用域实例解析失败求助
我有一个使用AnyConcreteTypeNotAlreadyRegisteredSource的Autofac容器,它允许无需显式注册即可解析任意具体类型。当我使用RegisterInstance方法在根容器注册具体类型的单例实例时,解析没有问题;但创建子生命周期作用域并完成相同的单例实例注册后,子作用域无法再解析该类型,会抛出以下异常:
Autofac.Core.Activators.Reflection.NoConstructorsFoundException: 'No accessible constructors were found for the type 'AutofacTests.IssueTests+Manager'.'
明明已经注册了该类型的单例实例,为什么还要尝试实例化对象?而且该问题似乎仅在解析具体类型而非接口时出现。以下是复现问题的测试代码:
using Autofac; using Autofac.Features.ResolveAnything; namespace AutofacTests { [TestClass] public class IssueTests { private interface IPerson { } private class Employee : IPerson { public Employee() { } } private class Manager : Employee { internal Manager(IPerson worker) { string s = ""; } } [TestMethod] public void Resolution_ThatWorks() { ContainerBuilder builder = new ContainerBuilder(); var mgr = new Manager(null); builder.RegisterInstance<IPerson>(mgr); builder.RegisterInstance<Manager>(mgr); using (var container = builder.Build()) { var person = container.Resolve<Manager>(); Assert.IsNotNull(person, "Employee could not be resolved!"); } } [TestMethod] public void ResolutionWithAnyTypeSource_ThatWorks() { ContainerBuilder builder = new ContainerBuilder(); // NOTE: This source registration does not impact the concrete type resolution when no child lifetime scope is involved. builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); var mgr = new Manager(null); builder.RegisterInstance<IPerson>(mgr); builder.RegisterInstance<Manager>(mgr); using (var container = builder.Build()) { var person = container.Resolve<Manager>(); Assert.IsNotNull(person, "Employee could not be resolved!"); } } [TestMethod] public void NestedResolution_ThatWorks() { ContainerBuilder builder = new ContainerBuilder(); using (var container = builder.Build()) { void RegisterNestedServices(ContainerBuilder builder) { var mgr = new Manager(null); builder.RegisterInstance<IPerson>(mgr); builder.RegisterInstance<Manager>(mgr); } using (var childContainer = container.BeginLifetimeScope(RegisterNestedServices)) { var person = childContainer.Resolve<Manager>(); Assert.IsNotNull(person, "Employee could not be resolved!"); } } } [TestMethod] public void NestedResolutionWithAnyTypeSource_ThatFails() { ContainerBuilder builder = new ContainerBuilder(); // NOTE: This source registration causes the resolution of a concrete type to fail in a child lifetime scope. builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); using (var container = builder.Build()) { void RegisterNestedServices(ContainerBuilder builder) { var mgr = new Manager(null); builder.RegisterInstance<IPerson>(mgr); builder.RegisterInstance<Manager>(mgr); } using (var childContainer = container.BeginLifetimeScope(RegisterNestedServices)) { // Works var person = childContainer.Resolve<IPerson>(); Assert.IsNotNull(person, "Person could not be resolved!"); // *** Fails *** var emp = childContainer.Resolve<Manager>(); Assert.IsNotNull(emp, "Employee could not be resolved!"); } } } } }
问题原因
这是AnyConcreteTypeNotAlreadyRegisteredSource的设计逻辑导致的:
- 根容器注册该类型源后,它会作用于所有子作用域。
- 子作用域注册具体类型单例时,Autofac的类型源检查逻辑存在优先级问题:
AnyConcreteTypeNotAlreadyRegisteredSource会先判断根容器中是否已注册该具体类型,而非当前子作用域的注册。 - 因为根容器里没有
Manager的注册(注册是在子作用域中添加的),所以类型源会尝试反射创建Manager实例,但Manager的构造函数是internal的,无法被Autofac访问,因此抛出异常。 - 解析接口
IPerson时不会触发问题,因为接口无法被类型源实例化,Autofac会直接查找子作用域中的注册。
解决方案
有三种可行的解决方式:
方式一:修改构造函数访问修饰符
把Manager的构造函数改成public,这样即使类型源尝试实例化,也能找到可用的构造函数。不过这种方式只是规避了异常,没有从根本上解决类型源的优先级问题。
public Manager(IPerson worker) { string s = ""; }
方式二:在子作用域注册时指定优先级
在注册子作用域的具体类型时,添加PreserveExistingDefaults(),确保Autofac优先使用子作用域中的注册,而不是类型源的动态注册:
void RegisterNestedServices(ContainerBuilder builder) { var mgr = new Manager(null); builder.RegisterInstance<IPerson>(mgr); builder.RegisterInstance<Manager>(mgr).PreserveExistingDefaults(); }
这个方法会让子作用域中的注册优先级高于类型源的动态注册,Autofac会直接返回你注册的单例实例,不会尝试反射创建。
方式三:使用自定义类型源替代原类型源
自定义类型源,让它在检查类型是否已注册时,优先检查当前作用域而非仅根容器。这样子作用域中的注册会被正确识别,类型源不会尝试实例化已注册的类型。
示例自定义类型源:
public class CurrentScopeAnyConcreteTypeSource : IRegistrationSource { public IEnumerable<IComponentRegistration> RegistrationsFor( Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor) { if (!(service is TypedService typedService)) yield break; var type = typedService.ServiceType; // 仅处理具体类型,跳过接口和抽象类 if (!type.IsConcrete()) yield break; // 检查当前作用域是否已有该类型的注册 var existingRegistrations = registrationAccessor(service); if (existingRegistrations.Any()) yield break; // 生成动态注册,和原类型源逻辑一致 var builder = RegistrationBuilder.ForType(type) .As(service) .InstancePerLifetimeScope(); yield return builder.CreateRegistration(); } public bool IsAdapterForIndividualComponents => false; }
然后在根容器中注册这个自定义类型源:
builder.RegisterSource(new CurrentScopeAnyConcreteTypeSource());
内容的提问来源于stack exchange,提问作者B Singh

