Autofac решает вопрос о регистрации ключей через IIndex - PullRequest
2 голосов
/ 26 сентября 2011

Это дополнительный вопрос к Настройка фабрики делегатов Autofac, определенной в абстрактном классе . Я реализовал предложение об использовании IIndex<K,V>, которое @Aren сделал в своем ответе, но я не могу преодолеть следующую ошибку:

Тестовый метод IssueDemoProject.WidgetTest.ProblemIllustration threw исключение: Autofac.Core.DependencyResolutionException: ни один из найдены конструкторы с 'Открытыми флагами привязки' 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 Ответ

3 голосов
/ 27 сентября 2011

Я думаю, вы ошибаетесь. В основном потому, что ваш 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. В противном случае это селектор экземпляра.

...