Concrete type instance registration in Autofac fails to resolve in a nested scope when using AnyConcreteTypeNotAlreadyRegisteredSource source

Viewed 18

I have an Autofac container that uses "AnyConcreteTypeNotAlreadyRegisteredSource" to allow resolution of any concrete type without explicit need to register it. If I register a singleton instance of a concrete type using the RegisterInstance method, there are no issues with the resolution. However, if I create a child lifetime scope and do the same concrete type singleton instance registration, the child lifetime scope is not able to resolve the type anymore. It throws the following exception:

Autofac.Core.Activators.Reflection.NoConstructorsFoundException: 'No accessible constructors were found for the type 'AutofacTests.IssueTests+Manager'.'

Why is it even trying to instantiate the object when I have a singleton instance of that type registered already? It seems like this problem only comes when I try to resolve a concrete type instead of an interface.

Here is the test code that demonstrates the problem (The test method name and comments show what works and what does not):

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!");
                }
            }
        }
    }
}

Anyone has any idea what am I doing wrong or how to make this work in the child lifetime scopes?

Thanks.

1 Answers

It appears you're hitting this known issue where AnyConcreteTypeNotAlreadyRegisteredSource (ACTNARS) doesn't properly look at new registrations in child scopes.

At the time of this writing there is not a fix for it, but we'd love a PR to get it fixed. It's been open for a while now.

Related