2

これは、抽象クラスで定義された Autofac デリゲート ファクトリの構成に対するフォローアップの質問です。@Aren が彼の回答で行った使用の提案を実装しましたIIndex<K,V>が、次のエラーを克服できません。

テスト メソッド IssueDemoProject.WidgetTest.ProblemIllustration が例外をスローしました: Autofac.Core.DependencyResolutionException: タイプ 'IssueDemoProject.WidgetWrangler' の 'パブリック バインディング フラグ' で見つかったコンストラクターはありません: 利用可能なサービスとパラメーターで呼び出すことができません: パラメーター 'IssueDemoProject を解決できません。コンストラクター 'Void .ctor(Autofac.IComponentContext, IssueDemoProject.WidgetType)' の WidgetType widgetType'。

更新: パラメータに基づいて異なる具体的なクラスを登録すると、それが機能することに注意してください。以下の 2 番目のテストを参照してください。

この問題を示すサンプル コードを次に示します。[編集: 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 の登録によって完全に解決できるコンストラクターが少なくとも 1 つ含まれている必要があります。または、解決操作に変数を明示的に渡す必要があります (このメソッドは反射的で非常に遅いことに注意してください)。

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 に答える