Autofac:在ContainerBuilder.Update()之后无法使用工厂解析依赖关系

时间:2015-10-27 16:01:34

标签: autofac

我的问题是我想使用 Func<>工厂解决依赖关系。如果我使用 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

     

通知&#34;工厂:1&#34;在这两种情况下。

我是否遗漏了某些东西,或者我必须在这种情况下创建我的cusom工厂?

P.S。

Autofac 3.5.2或4.0 beta 8-157 .net 4.5.1

1 个答案:

答案 0 :(得分:0)

不幸的是,这是设计原因,我不知道。查看Autofac代码可以更好地了解他们如何使用相同的接口定义注册项目,简而言之,所有注册都得到维护,但最后一次注册获胜(ref)。等等......奇怪的是,对于 Fun&lt; ...&gt; ,你实际上是按顺序得到它们。您可以通过将 TestConsumer 类的构造函数更改为:

来轻松进行测试
public TestConsumer(Func<ITest> testFactory, IEnumerable<Func<ITest>> testFactories, IEnumerable<ITest> tests, ITest test, ITestFactory customFactory)
{
    // ...
}

请注意,您将获得所有Func和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()

完全相反的方法不是很好吗