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

使用AnyConcreteTypeNotAlreadyRegisteredSource时Autofac嵌套作用域实例解析失败求助

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 03:15:42