web-dev-qa-db-ja.com

SimpleInjectorのコンテナインスタンスを取得します

ASP.NETMVCプロジェクトでSimpleInjectorを使用しています。 SimpleInjector.Integration.Web.Mvcnugetパッケージを追加しました。これにより、App_StartフォルダーにSimpleInjectorInitializerクラスが追加され、DIが初期化されます。コードは次のようになります

public static void Initialize()
{
    // Did you know the container can diagnose your configuration? 
    // Go to: https://simpleinjector.org/diagnostics
    var container = new Container();

    //Container configuration code
    DependencyResolver.SetResolver(
        new SimpleInjectorDependencyResolver(container));
}

これにより、MVCコントローラーのDIが正しく構成されます。

私の質問は、コントローラー\クラスのいずれかにあるコンテナーのインスタンスを取得して依存関係を手動で解決したい場合、どうすればよいですか?.

私は以前にAutoFacに取り組んだことがあり、依存インターフェースIComponentContextがあり、手動で解決を行う必要がある任意のクラスに挿入できます。

更新

これがシナリオです。私のコントローラーは、初期化がコントローラーメソッドで渡された入力パラメーターに依存するサービスを使用しているため、構築時に依存関係をインスタンス化できません。

これはDIのアンチパターンであると理解していますが、いくつかの場所で必要になるため、DIコンテナを注入することが次善の策です。単純なインジェクターのサンプルでは、​​静的変数を使用して、回避したいコンテナーを共有する必要があります。また、SimpleInjectorInitializerの動作方法では不可能です。

16
Chandermani

アプリケーションの起動パスの一部であるコードを除いて、コードはコンテナー(またはコンテナーの抽象化、コンテナーのファサードなど)に直接依存してはなりません。このパターンは Service Locator と呼ばれ、 Mark Seemann には 良い説明 これが悪い考えである理由があります。

したがって、コンポーネント(コントローラーなど)はコンテナーに直接依存しないでください。これにより、使用される依存関係が非表示になり、クラスのテストが困難になります。さらに、コードは外部フレームワークに依存し始め(変更が難しくなります)、または知る必要のない抽象化に依存し始めます。

私のコントローラーは、初期化がコントローラーメソッドで渡された入力パラメーターに依存するサービスを使用しているため、構築時に依存関係をインスタンス化できません

この問題には一般的なパターンがあります: 抽象ファクトリデザインパターン 。ファクトリパターンを使用すると、型の作成を遅らせたり、特定の型を構築するための追加のランタイムパラメータを渡すことができます。これを行うと、コントローラーはコンテナーに依存する必要がなくなり、単体テストで構築されたコンテナーを渡す必要がなくなります(通常、単体テストプロジェクトではDIフレームワークを使用しないでください)。

ただし、コンポーネントに 作成中のランタイムデータはコードの臭いです を要求させることに注意してください。それをしないでください。

これを行うことで、問題をファクトリ実装に移しているだけだと思われるかもしれません。コンテナへの依存関係をファクトリ実装に移動していますが、ファクトリ実装はアプリケーションの Composition Root の一部になるため、実際には問題を解決しています。これにより、アプリケーションコード自体が誰にも気付かれなくなります。 DIフレームワーク。

したがって、これがコードを構造化するようにアドバイスする方法です。

_// Definition of the factory in the UI or BL layer
public interface ISomeServiceFactory
{
    ISomeService Create(int inputParameter);
}

// Controller depending on that factory:
public class MyController : Controller
{
    private readonly ISomeServiceFactory factory;

    public MyController(ISomeServiceFactory factory)
    {
        this.factory = factory;
    }

    public ActionResult Index(int value)
    {
        // here we use that factory
        var service = this.factory.Create(value);
    }
}
_

コンポジションルート(スタートアップパス)で、ファクトリ実装とその登録を定義します。

_private class SomeServiceFactory : ISomeServiceFactory
{
    private readonly Container container;

    // Here we depend on Container, which is fine, since
    // we're inside the composition root. The rest of the
    // application knows nothing about a DI framework.
    public SomeServiceFactory(Container container)
    {
        this.container = container;
    }

    public ISomeService Create(int inputParameter)
    {
        // Do what ever we need to do here. For instance:
        if (inputParameter == 0)
            return this.container.GetInstance<Service1>();
        else
            return this.container.GetInstance<Service2>();
    }
}

public static void Initialize()
{
    var container = new Container();

    container.RegisterSingle<ISomeServiceFactory, SomeServiceFactory>();
}
_

作成時に、Containerはそれ自体を登録するため(呼び出しRegisterSingle<Container>(this)を使用)、いつでもコンテナーを任意のコンポーネントに挿入できます。これは、Autofacを使用するときにIComponentContextを挿入するのと似ています。ただし、Autofac、Simple Injector、およびその他のコンテナについても同じことが言えます。コンポジションルートの外側にあるコンポーネントにコンテナを注入する必要はありません(その理由はほとんどありません)。

31
Steven