2015-10-27 3 views
2

Моя проблема в том, что я хочу использовать Func <> завод для разрешения зависимости. И если я использую ContainerBuilder Update() (мне это нужно для издевательства над некоторыми сервисами в тестах интеграции), эта фабрика по-прежнему разрешает устаревшие экземпляры.Autofac: невозможно разрешить зависимость с использованием фабрики после ContainerBuilder.Update()

Я создал простой сценарий, чтобы воспроизвести проблему:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var containerBuilder = new ContainerBuilder(); 
     containerBuilder.RegisterType<Test>().As<ITest>(); 
     containerBuilder.RegisterType<Test1Factory>().As<ITestFactory>(); 
     containerBuilder.RegisterType<TestConsumer>().AsSelf(); 
     var container = containerBuilder.Build(); 

     var tc1 = container.Resolve<TestConsumer>(); 

     var cbupdater = new ContainerBuilder(); 
     cbupdater.RegisterType<Test2>().As<ITest>(); 
     cbupdater.RegisterType<Test2Factory>().As<ITestFactory>(); 
     cbupdater.Update(container); 

     var tc2 = container.Resolve<TestConsumer>(); 

     Console.ReadLine(); 
    } 
} 

public interface ITest 
{ 
    int Id { get; set; } 
} 

public class Test : ITest 
{ 
    public Test() 
    { 
     Id = 1; 
    } 

    public int Id { get; set; } 
} 

public class Test2 : ITest 
{ 
    public Test2() 
    { 
     Id = 2; 
    } 

    public int Id { get; set; } 
} 

public interface ITestFactory 
{ 
    ITest Create(); 
} 

public class Test1Factory : ITestFactory 
{ 
    public ITest Create() 
    { 
     return new Test(); 
    } 
} 

public class Test2Factory : ITestFactory 
{ 
    public ITest Create() 
    { 
     return new Test2(); 
    } 
} 

public class TestConsumer 
{ 
    public TestConsumer(Func<ITest> testFactory, ITest test, ITestFactory customFactory) 
    { 
     Console.WriteLine("factory: " + testFactory().Id); 
     Console.WriteLine("direct: " + test.Id); 
     Console.WriteLine("MyCustomFactory: " + customFactory.Create().Id); 
     Console.WriteLine("*************"); 
     Console.WriteLine(); 
    } 
} 

Выход есть:

завод: 1 прямой: 1 MyCustomFactory: 1


завод: 1 прямой : 2 MyCustomFactory: 2


Уведомление: Завод "1" в обоих случаях.

Я что-то упускаю или мне нужно создать свою фабрику кузсов в этом сценарии?

P.S.

Autofac 3.5.2 или 4.0 бета 8-157 .net 4.5.1

ответ

0

Это дизайн, к сожалению, причины, я не знаю. Глядя на код Autofac, вы можете лучше понять, как они регистрируют элементы с тем же определением интерфейса, одним словом, все регистрации поддерживаются, но последняя регистрация выигрывает (ref). Подождите ... это еще не все, странно, для Fun < ...>, вы на самом деле их на заказ. Вы можете легко проверить, изменив конструктор классаTestConsumer к:

public TestConsumer(Func<ITest> testFactory, IEnumerable<Func<ITest>> testFactories, IEnumerable<ITest> tests, ITest test, ITestFactory customFactory) 
{ 
    // ... 
} 

Обратите внимание, что вы получите все Funcs и регистрации ITEST. Вы просто повезло, что разрешение ITest напрямую разрешено Test2.

Теперь, сказав все вышеизложенное, существует способ described here. Вы должны создать контейнер без регистрации вы хотите переопределить, поэтому:

/// <summary> 
/// This has not been tested with all your requirements 
/// </summary> 
private static IContainer RemoveOldComponents(IContainer container) 
{ 
    var builder = new ContainerBuilder(); 
    var components = container.ComponentRegistry.Registrations 
         .Where(cr => cr.Activator.LimitType != typeof(LifetimeScope)) 
         .Where(cr => cr.Activator.LimitType != typeof(Func<ITest>)); 
    foreach (var c in components) 
    { 
     builder.RegisterComponent(c); 
    } 

    foreach (var source in container.ComponentRegistry.Sources) 
    { 
     builder.RegisterSource(source); 
    } 
    return builder.Build(); 
} 

И вы можете просто изменить свой основной метод следующим:

static void Main(string[] args) 
{ 
    var containerBuilder = new ContainerBuilder(); 
    containerBuilder.RegisterType<Test>().As<ITest>(); 
    containerBuilder.RegisterType<Test1Factory>().As<ITestFactory>(); 
    containerBuilder.RegisterType<TestConsumer>().AsSelf(); 
    var container = containerBuilder.Build(); 

    var tc1 = container.Resolve<TestConsumer>(); 

    container = RemoveOldComponents(container); 

    var cbupdater = new ContainerBuilder(); 
    cbupdater.RegisterType<Test2>().As<ITest>(); 
    cbupdater.RegisterType<Test2Factory>().As<ITestFactory>(); 
    cbupdater.Update(container); 

    var tc2 = container.Resolve<TestConsumer>(); 

    Console.ReadLine(); 
} 

PS: Не было бы здорово иметь метод, который делает полную противоположность PreserveExistingDefaults()

Смежные вопросы