Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autofac ignores registration conflicts

I have the following test

[TestFixture]
public class Test
{
    public interface IMy { }

    class MyClass : IMy { }

    class MyClass2 : IMy { }

    [Test]
    public static void Go()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<MyClass>().AsImplementedInterfaces();
        builder.RegisterType<MyClass2>().AsImplementedInterfaces();
        var container = builder.Build();
        var resolved = container.Resolve<IMy>();
        Console.WriteLine(resolved);
    }
}

Why it does not throw exception when implementations are obviously in conflict ? And how to make it throw exception if such a conflict found ?

UPDATE Solution with registration checking is almost Ok, but there is simple situation when it fails:

[TestFixture]
public class Test
{
    public interface IPlugin
    {
    }

    public interface IMy
    {

    }

    class MyClass : IMy, IPlugin
    {
        public void Dispose()
        {
        }
    }

    class MyClass2 : IPlugin
    {
        public void Dispose()
        {
        }
    }

    public class SingleRegistrationModule : Module
    {
        protected override void AttachToComponentRegistration(
            IComponentRegistry componentRegistry, 
            IComponentRegistration registration)
        {
            foreach (var service in registration.Services)
            {
                var registrations = componentRegistry.RegistrationsFor(service);
                if (registrations.Count() > 1)
                {
                    throw new Exception(
                        "Can't register '{registration.Activator.LimitType}' as '{service}'" + 
                        " because '{registrations.First().Activator.LimitType}' is already registered");
                }
            }
        }
    }

    [Test]
    public static void Go()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<MyClass>().AsImplementedInterfaces();
        builder.RegisterType<MyClass2>().AsImplementedInterfaces();
        builder.RegisterModule<SingleRegistrationModule>();
        var container = builder.Build();
        var resolved = container.Resolve<IMy>();
        Console.WriteLine(resolved);
    }
}

In this case nobody resolves IInitializable so it is acceptable to have multiple implementations. Moreover there are scenarios when mulltiple implementation is OK, for example IPluginToSomething

like image 924
Alex Ilyin Avatar asked Feb 07 '23 01:02

Alex Ilyin


1 Answers

The reason that Autofac doesn't throw an exception is because Autofac considers multiple registrations for the same interface to be part of a collection. Example:

builder.RegisterType<MyClass>().As<IMy>();
builder.RegisterType<MyClass2>().As<IMy>();
var container = builder.Build();
var collection = container.Resolve<IEnumerable<IMy>>();
Console.WriteLine(collection.Count()); // prints "2"

In case multiple registrations are made, a call to Resolve<IMy>() will resolve only one of them (either the first or the last, but I always forget which one it is). I personally consider this a design flaw in Autofac (and the other DI containers), because this causes your application to fail silently, instead of failing fast. In Simple Injector the choice has been made to strictly separate the registrations of collections (as explained here) to prevent these types of configuration mistakes.

like image 108
Steven Avatar answered Feb 19 '23 04:02

Steven