简体   繁体   English

Autofac忽略了注册冲突

[英]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: 注册检查的UPDATE解决方案几乎没问题,但是失败的情况很简单:

[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. 在这种情况下,没有人解析IInitializable,因此可以接受多个实现。 Moreover there are scenarios when mulltiple implementation is OK, for example IPluginToSomething 此外,有些情况下mulltiple实现正常,例如IPluginToSomething

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. Autofac不会抛出异常的原因是因为Autofac认为同一接口的多个注册是集合的一部分。 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). 如果进行多次注册,调用Resolve<IMy>()将只解析其中一个(第一个或最后一个,但我总是忘记它是哪一个)。 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. 我个人认为这是Autofac(以及其他DI容器)中的设计缺陷,因为这会导致应用程序无声地失败,而不是快速失败。 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. 简单的喷油器的选择,已作出严格区分藏品的登记(如解释在这里 ),以防止这些类型的配置错误。

As Steven said, Autofac will consider multiple registration of a same service to be part of a collection. 正如Steven所说, Autofac将考虑将同一服务的多个注册作为集合的一部分。

If you don't want this behavior, you can add a check using an Autofac module : 如果您不想要此行为,可以使用Autofac模块添加检查:

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

Then you can register the module using : 然后您可以使用以下方式注册模块:

builder.RegisterModule<SingleRegistrationModule>();

The exception will be thrown when the container is being built. 构建容器时将抛出异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM