2016-05-10 5 views
2

У меня есть следующий тестAutofac игнорирует регистрацию конфликтов

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

Почему это не выбрасывает исключение при реализации, очевидно, в конфликте? И как заставить это бросать исключение, если такой конфликт найден?

UPDATE Решение с проверкой регистрации почти Ok, но там просто ситуация, когда не удается:

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

В этом случае никто не решает IInitializable поэтому допустимо иметь несколько реализаций. Кроме того, существуют сценарии, когда реализация mulltiple в порядке, например, IPluginToSomething

ответ

3

Причина, по которой Autofac не генерирует исключение, заключается в том, что Autofac рассматривает несколько регистраций для того же интерфейса, которые должны быть частью коллекции. Пример:

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" 

В случае сделаны множественные регистрации, вызов Resolve<IMy>() будет решить только один из них (либо первый или последний, но я всегда забываю, какая она есть). Я лично считаю это недостатком дизайна в Autofac (и других контейнерах DI), потому что это заставляет ваше приложение терпеть неудачу молча, а не быстро. В Simple Injector был сделан выбор, чтобы строго отделить регистрацию коллекций (как объяснено here), чтобы предотвратить эти ошибки конфигурации.

+0

Я полностью согласен с вами, и я также попытался разрешить сбор, но как отменить это ошибочное поведение по умолчанию? –

+0

@AlexIlyin: Я не думаю, что вы можете легко переопределить это, но, возможно, гуру Autofac может ответить на это. – Steven

1

Как Steven сказал, Autofac рассмотрит возможность одновременной регистрации одной и той же услуги в составе коллекции.

Если вы не хотите этого, вы можете добавить проверку с помощью модуля 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"); 
      } 
     } 
    } 
} 

Затем вы можете зарегистрировать модуль с помощью:

builder.RegisterModule<SingleRegistrationModule>(); 

Исключение бросать, когда контейнер строится.

+0

Спасибо за ответ, но, похоже, проблема. Это решение бросает слишком много исключений ... Предположим, что типы зарегистрированы с использованием .RegisterType () .AsImplementedInterfaces(); И некоторые из них поддерживают IDisposable, IEnumerable или любой другой интерфейс, который никто не собирается решать. Это решение сообщит об ошибке для этих интерфейсов. –

+0

Конечно, я могу поддерживать список интерфейсов исключений, но это не масштабируемое решение. –

+0

Я даже пытался прослушивать такие выражения, как ResolveOperationBeginning и InstanceLookupBeginning, но эти события, похоже, не отображают запрошенный интерфейс. –