web-dev-qa-db-ja.com

Web APIコントローラーに依存関係を挿入できない単純なインジェクター

Simple Injectorで基本的なコンストラクターDIを実行しようとしていますが、Web APIコントローラーの依存関係を解決できないようです。

  • 「コントローラー」フォルダーの外側にある「API」フォルダーにAPIコントローラーがあります。
  • また、「Controllers」フォルダー内に配置しようとしましたが、それは大きな違いをもたらさないようです。私が受け取るスタックトレースは、 この質問 に示されているものに似ています。
  • 「Simple Injector MVC Integration Quick Start」NuGetパッケージ(v。2.1.0)の新規インストールを使用しています。
  • ドキュメントのベースSimpleInjectorWebApiDependencyResolverがあります。これは、見つかった here と同じです。
  • Entity Frameworkを使用しており、コンテキストを正しく読み込むための変更について discussion thread を見てきました。

これは問題ではないようですが、次のエラーが表示されます。

タイプ「MyProject.API.ArticleController」にはデフォルトのコンストラクタがありません

System.ArgumentException at

System.Web.Http.Internal.TypeActivator.Create [TBase](Type instanceType)のSystem.Linq.Expressions.Expression.New(Type type)System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request、Type controllerType) 、Func`1&アクティベーター)System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request、HttpControllerDescriptor controllerDescriptor、Type controllerType)で

現在の状態/呼び出し順序から何かを変更する必要があるかどうかについて、誰かが私にいくつかの提案を提供できれば幸いです。

ArticleController(基本構造):

public class ArticleController : ApiController
{
    private readonly IArticleRepository articleRepository;
    private readonly IUserRepository userRepository;
    private readonly IReleaseRepository releaseRepository;

    public ArticleController(IArticleRepository articleRepository, IUserRepository userRepository, IReleaseRepository releaseRepository)
    {
        this.articleRepository = articleRepository;
        this.userRepository = userRepository;
        this.releaseRepository = releaseRepository;
    }

    // GET api/Article
    public IEnumerable<Article> GetArticles(){ // code }

    // GET api/Article/5
    public Article GetArticle(int id){ // code }

    // PUT api/Article/5
    public HttpResponseMessage PutArticle(int id, Article article){ // code }

    // POST api/Article
    public HttpResponseMessage PostArticle(ArticleModel article){ // code }

    // DELETE api/Article/5
    public HttpResponseMessage DeleteArticle(int id){ // code }
}

SimpleInjectorInitializer:

public static class SimpleInjectorInitializer
{
    public static void Initialize()
    {
        var container = new Container();
        InitializeContainer(container);
        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        container.RegisterMvcAttributeFilterProvider();
        container.Verify();

        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
    }

    private static void InitializeContainer(Container container)
    {
        container.Register<IArticleRepository, ArticleRepository>();
        container.Register<IUserRepository, UserRepository>();
        container.Register<IReleaseRepository, ReleaseRepository>();
    }
}

Global.asax.cs:

public class WebApiApplication : System.Web.HttpApplication
{
    private void ConfigureApi()
    {
        // Create the container as usual.
        var container = new Container();

        // Verify the container configuration
        // container.Verify();

        // Register the dependency resolver.
        GlobalConfiguration.Configuration.DependencyResolver =
                new SimpleInjectorWebApiDependencyResolver(container);
    }

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        ConfigureApi();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}
39
user1417835

TLTR:この問題は、Web APIがコントローラータイプの解決を処理する暗黙的な方法が原因です。 Web APIコントローラーを明示的に登録すると、問題の場所がわかります。

以下は、裏側で何が起こっているかを段階的に示しています。

  1. _System.Web.Http.DefaultHttpControllerActivator_はSimpleInjectorWebApiDependencyResolverを呼び出し、APIコントローラーの作成を要求します。
  2. SimpleInjectorWebApiDependencyResolverは、その呼び出しを_SimpleInjector.Container_インスタンスに転送します。
  3. ただし、そのContainerインスタンスには、そのAPIコントローラーの明示的な登録はありません(空のコンテナーをリゾルバーに提供したため)。
  4. 明示的な登録がないため、コンテナはそのタイプの最後の登録を試行します。
  5. ただし、そのコントローラータイプは、コンテナーに登録されていないため解決できないインターフェイスに依存します(コンテナーが空であることを思い出してください)。
  6. コンテナは通常例外をスローしますが、この場合は__が返されます。これは、タイプが_IServiceProvider.GetService_メソッドを介して要求され、タイプが明示的に登録されていないためです。
  7. SimpleInjectorWebApiDependencyResolverGetServiceメソッドもnullを返します。定義によりnullを返す必要があるためです。登録が存在しない場合はnullを返す必要があります(現在のところ)。
  8. DependencyResolverはnullを返したため、DefaultHttpControllerActivatorはデフォルトの動作に戻ります。つまり、そのタイプ自体を作成しますが、これにはコントローラーにデフォルトのコンストラクターが必要です。

要するに、この問題は、Web APIがコントローラータイプの解決を暗黙的に処理するために発生します。

したがって、ここでの解決策は次のとおりです。

  1. Webアプリケーションに単一のContainerのみがあります。これにより、あらゆる種類のトラブルや設定の複雑化を防ぎます。
  2. コンテナにすべてのWeb API Controllerを明示的に登録します。コントローラーを明示的に登録すると、コントローラーを解決できない場合にSimple Injectorが例外をスローするようになります。また、これにより、container.Verify()を呼び出すことができます。これにより、構成が無効な場合、起動時にアプリケーションが失敗します( 検証可能な構成が重要 )。また、これにより 構成の診断 が可能になり、構成の正確さについてさらに自信が持てます。

アドバイスはMVCとWeb APIを独自のプロジェクトに配置することです 。これにより、物事がずっと簡単になります。

すべてのWeb APIコントローラーの登録は、次のコードで実行できます。

_container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
_

UPDATE:

このエラーは非常に一般的であるため、新しいバージョンのSimpleInjectorWebApiDependencyResolverクラスは、コントローラーが入力されたときに単にnevernullを返します。要求されます。代わりに、説明的なエラーがスローされます。このため、公式のSimpleInjectorWebApiDependencyResolverを使用している限り、エラーは表示されなくなります。

43
Steven