2

这是配置在抽象类上定义的 Autofac 委托工厂的后续问题。我已经实施了使用IIndex<K,V>@Aren 在他的回答中提出的建议,但我无法克服以下错误:

测试方法 IssueDemoProject.WidgetTest.ProblemIllustration 抛出异常:Autofac.Core.DependencyResolutionException:无法使用可用服务和参数调用类型“IssueDemoProject.WidgetWrangler”的“公共绑定标志”的构造函数:无法解析参数“IssueDemoProject。构造函数 'Void .ctor(Autofac.IComponentContext, IssueDemoProject.WidgetType)' 的 WidgetType 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;
    }    }
}
4

1 回答 1

3

我认为你做错了。主要是因为您WidgetWrangler期望工厂(IIndex<,>实际上成为您的工厂)和 WidgetType。

例外是因为您的WidgetType枚举在 Autofac 中没有默认注册,它可能不应该。Autofac 无法弄清楚要传递给构造函数的值,因为我猜您正在尝试使用 Autofac 来解决您的WidgetWrangler.

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

我假设整个事情的目标是有某种方法来检索Widget给定WidgetType某个地方的新子类。如果是这种情况,那么您只需要IIndex<,>.

无论您需要在哪里创建新的小部件,您只需使用 IIndex<,> 类型。这很简单。

如果您需要在实例化的同时执行另一个操作,您应该将 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注册。否则它是一个实例选择器。

于 2011-09-26T22:04:16.393 回答