Autofac - コンテナを渡さなくてもランタイム パラメータを解決する

はい、どこにでもコンテナを渡すことはアンチパターンです。

次のようなファクトリを使用することで回避できます:

(注:この回答のすべてのコードはテストされていません。Visual Studio のないマシンのテキスト エディターでこれを書いています)

public interface IServiceHelperFactory
{
    IServiceHelper CreateServiceHelper(string serviceName);
}

public class ServiceHelperFactory : IServiceHelperFactory
{
    private IContainer container;

    public ServiceHelperFactory(IContainer container)
    {
        this.container = container;
    }

    public IServiceHelper CreateServiceHelper(string serviceName)
    {
        return container.Resolve<ServiceHelper>(new NamedParameter("serviceName", serviceName));
    }
}

起動時に、ServiceHelperFactory を登録します 他のすべてと同様に、Autofac で:

builder.RegisterType<ServiceHelperFactory>().As<IServiceHelperFactory>();

次に、ServiceHelper が必要な場合 別の場所では、コンストラクタ インジェクションによってファクトリを取得できます:

public class SomeClass : ISomeClass
{
    private IServiceHelperFactory factory;

    public SomeClass(IServiceHelperFactory factory)
    {
        this.factory = factory;
    }

    public void ThisMethodCreatesTheServiceHelper()
    {
        var helper = this.factory.CreateServiceHelper("some service name");
    }
}

Autofac を使用してコンストラクター インジェクションを介してファクトリ自体を作成することにより、自分でコンテナーを渡す必要なく、ファクトリーがコンテナーについて認識していることを確認できます。

確かに、一見すると、このソリューションは、コンテナーを直接渡す場合とあまり変わらないように見えます。ただし、利点は、アプリがコンテナーから分離されていることです。コンテナーが認識される唯一の場所 (スタートアップを除く) は、ファクトリ内です。

編集:

わかりました、忘れました。上で述べたように、Visual Studio のないマシンでこれを書いているため、サンプル コードをテストできません。
あなたのコメントを読んで、Autofac を使用してコンテナー自体を登録しようとしたときに同様の問題が発生したことを思い出しました。

私の問題は、コンテナーをビルダーに登録する必要があることでした。
しかし、コンテナー インスタンスを登録するには、builder.Build() を呼び出す必要がありました。 ...コンテナを作成します。つまり、後でビルダーに登録することはできません。
表示されたエラー メッセージは覚えていませんが、同じ問題が発生していると思います。

私が見つけた解決策は、2 つ目のビルダーを作成し、そこにコンテナーを登録し、2 つ目のビルダーを使用して唯一のコンテナーを更新することでした。 .

以下は、私のオープン ソース プロジェクトの 1 つからの作業コードです:

起動時にコンテナを登録します::

var builder = new ContainerBuilder();

// register stuff here

var container = builder.Build();

// register the container
var builder2 = new ContainerBuilder();
builder2.RegisterInstance<IContainer>(container);
builder2.Update(container);

...これは WindowService によって使用されます 新しい WPF ウィンドウを作成するには:

public class WindowService : IWindowService
{
    private readonly IContainer container;

    public WindowService(IContainer container)
    {
        this.container = container;
    }

    public T GetWindow<T>() where T : MetroWindow
    {
        return (T)this.container.Resolve<T>();
    }
}

上記のアプローチの道をたどり、正常に動作しましたが、IContainer の「Resolve<>」メソッドが拡張メソッドであるため、単体テストが不可能であることがわかりました。また、コンテナを周りに渡さないというすべての話で、それは本当に「正しい」とは感じませんでした.

私は製図板に戻り、Autofac Delegate Factoryhttp://docs.autofac.org/en/latest/advanced/delegate-factories.html を使用してオブジェクトをインスタンス化する「正しい」方法を見つけました。


解決は、ルート構成オブジェクトに対してのみ発生する必要があります。解決を呼び出すことは、匂いテストであるオブジェクトを「新しくする」こととほぼ同じです。解像度が動的で、オンザフライでしか決定できない場合もありますが、ほとんど 依存関係は決定論的であり、事前に登録できます。これを Autofac でどのように行うかが課題です。 @Christian Specht による授与された回答は良い回答ですが、実行時にすべてが決定されることを前提としています。

設計時に依存関係チェーンを定義するには、SO トピック Autofac サブ依存関係チェーンの登録を参照してください...