Autofac通过索引解决有关密钥注册的问题

时间:2011-09-26 15:26:17

标签: autofac resolve

这是Configuring an Autofac delegate factory that's defined on an abstract class的后续问题。我已经实现了@Aren在他的回答中使用IIndex<K,V>的建议,但我无法克服以下错误:

  

测试方法IssueDemoProject.WidgetTest.ProblemIllustration投掷   异常:Autofac.Core.DependencyResolutionException:无   在类型上使用“公共绑定标志”找到的构造函数   可以使用available调用'IssueDemoProject.WidgetWrangler'   服务和参数:无法解析参数   'IssueDemoProject.WidgetType widgetType'的构造函数'Void   .ctor(Autofac.IComponentContext,IssueDemoProject.WidgetType)'。

更新:应该注意的是,如果我根据参数注册不同的具体类,那就有效。请参阅下面的第二个测试。

以下是一些说明问题的示例代码。 [编辑:我更新了相同的内容以使用IIndex查找。]

有人能告诉我我做错了吗?

using Autofac;
using Autofac.Features.Indexed;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace IssueDemoProject
{

public enum WidgetType
{
    Sprocket,
    Whizbang
}

public class SprocketWidget : Widget
{
}

public class WhizbangWidget : Widget
{
}

public abstract class Widget
{
}

public class WidgetWrangler : IWidgetWrangler
{
    public Widget Widget { get; private set; }

    public WidgetWrangler(IComponentContext context, WidgetType widgetType)
    {
        var lookup = context.Resolve<IIndex<WidgetType, Widget>>();
        Widget = lookup[widgetType];
    }
}

public interface IWidgetWrangler
{
    Widget Widget { get; }
}

[TestClass]
public class WidgetTest
{
    // NOTE: This test throws the exception cited above
    [TestMethod]
    public void ProblemIllustration()
    {
        var container = BuildContainer(
            builder =>
                {
                    builder.RegisterType<WidgetWrangler>().Keyed<IWidgetWrangler>(WidgetType.Sprocket).
                        InstancePerDependency();
                    builder.RegisterType<WidgetWrangler>().Keyed<IWidgetWrangler>(WidgetType.Whizbang).
                        InstancePerDependency();
                }
            );

        var lookup = container.Resolve<IIndex<WidgetType, IWidgetWrangler>>();
        var sprocketWrangler = lookup[WidgetType.Sprocket];
        Assert.IsInstanceOfType(sprocketWrangler.Widget, typeof(SprocketWidget));

        var whizbangWrangler = container.ResolveKeyed<IWidgetWrangler>(WidgetType.Whizbang);
        Assert.IsInstanceOfType(whizbangWrangler.Widget, typeof(WhizbangWidget));
    }

    // Test passes
    [TestMethod]
    public void Works_with_concrete_implementations()
    {
        var container = BuildContainer(
            builder =>
                {
                    builder.RegisterType<SprocketWidget>().Keyed<Widget>(WidgetType.Sprocket).
                        InstancePerDependency();
                    builder.RegisterType<WhizbangWidget>().Keyed<Widget>(WidgetType.Whizbang).
                        InstancePerDependency();
                });

        var lookup = container.Resolve<IIndex<WidgetType, Widget>>();
        var sprocketWrangler = lookup[WidgetType.Sprocket];
        Assert.IsInstanceOfType(sprocketWrangler, typeof(SprocketWidget));

        var whizbangWrangler = container.ResolveKeyed<Widget>(WidgetType.Whizbang);
        Assert.IsInstanceOfType(whizbangWrangler, typeof(WhizbangWidget));
    }

    private IComponentContext BuildContainer(Action<ContainerBuilder> additionalRegistrations)
    {
        var assembly = GetType().Assembly;
        var builder = new ContainerBuilder();
        builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();
        builder.RegisterAssemblyTypes(assembly).AsSelf();
        additionalRegistrations(builder);
        IComponentContext container = builder.Build();
        return container;
    }    }
}

1 个答案:

答案 0 :(得分:3)

我认为你错了。主要是因为您的WidgetWrangler期望工厂(IIndex<,>有效地成为您的工厂)和WidgetType。

异常是因为您的WidgetType枚举在Autofac中没有默认注册,而且可能不应该。 Autofac无法确定要传递给构造函数的值,因为我猜你正在尝试使用Autofac来解析你的WidgetWrangler

为了从Autofac中解析某些内容,它必须至少包含一个可以通过Autofac注册完全解析的构造函数,或者显式地将变量传递给解析操作(请注意,此方法具有反射性且非常慢)。

我假设整个事情的目标是有一些方法来检索Widget某个WidgetType的新子类。如果是这种情况,则所有您需要的是IIndex<,>

无论您何时需要创建新的小部件,只需使用IIndex&lt;,&gt;类型。这很简单。

如果您需要在实例化的同时执行另一个操作,则应将IIndex包装在使用IIndex的工厂类中,然后执行操作。

例如:

class Widget
{
    // Stuff...

    public virtual void Configure(XmlDocument xmlConfig)
    {
        // Config Stuff
    }
}

interface IWidgetFactory
{
     Widget Create(WidgetType type, XmlDocument config);
}

class WidgetFactory : IWidgetFactory
{
    private readonly IIndex<WidgetType, Widget> _internalFactory;
    public WidgetFactory(IIndex<WidgetType, Widget> internalFactory)
    {
        if (internalFactory == null) throw new ArgumentNullException("internalFactory");
        _internalFactory = internalFactory;
    }

    public Widget Create(WidgetType type, XmlDocument config)
    {
        Widget instance = null;
        if (!_internalFactory.TryGetValue(type, out instance))
        {
            throw new Exception("Unknown Widget Type: " + type.ToString);
        }

        instance.Configure(config);

        return instance;
    }
}

然后您可以简单地使用包装工厂:

class SomethingThatNeedsWidgets
{
    private readonly IWidgetFactory _factory;
    public SomethingThatNeedsWidgets(IWidgetFactory factory)
    {
        if (factory == null) return new ArgumentNullException("factory");
        _factory = factory;
    }

    public void DoSomething()
    {
        Widget myInstance = _factory.Create(WidgetType.Whizbang, XmlDocument.Load("config.xml"));

        // etc...
    }
}

请记住,如果除了获取Widget实例之外不需要执行任何操作,则IIndex<WidgetType, Widget>本身就是一个工厂。 (假设所有已注册的Widget子类都已注册InstancePerDependency。否则它是一个实例选择器。