質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.04%

SimpleInjectorを使って、ASP.net MVC | WebAPI の両方のコントローラーを持つWebアプリケーションを動作させたい。

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 295

manzyun

score 2133

 概要

タイトル通りです。

ASP.netでMVCによるWebブラウザで閲覧できるページと、WebAPIによるAPIアクセスを可能とするWebサービスを作っており、SimpleInjectorで各機能への依存性の注入を行っております。

Webページで見られる機能については正常に動作するものの、WebAPIの方は、

System.ArgumentException が発生しました。
Message: 例外がスローされました: 'System.ArgumentException' (System.Core.dll の中)
追加情報:型 'MyWebApp.Controllers.GenerateUniqueIdController' には既定のコンストラクターがありません

と例外が発生しております。

なお、.netFramework 4で制作しております。

詳細

クリーンアーキテクチャを設計・開発に導入し、依存性注入を行ってテストの容易性を高めるということを行っていたりしております。

そのため、各ユースケースやリポジトリに対してはSimpleInjectorを利用し、データの実態を注入しているのですが、[概要]の例外が発生し、APIを作ることができていない状態です。

やりたいこと

この例外を解決し、WebAPIを設置し、Webページ側からJavaScriptのAjaxなどでAPIを叩き、値を取得するようにしたいです。

ソースコード(一部)

「Front」プロジェクト内

Global.asax.cs

/* using 省略 */

namespace MyWebApp
{
    public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            XmlConfigurator.Configure(new FileInfo(Server.MapPath("~/Web.config")));
            DIRegister.Regist();
            DapperModelInitializer.Initialize();
        }
/* 以下略 */

Controller/GenerateUniqueIdController.cs

/* using省略 */

namespace MyWebApp.Controllers
{
    public class GenerateUniqueIdController : ApiController
    {
        private readonly IGenerateUniqueIdUseCase _IdUseCase;

        public GenerateUniqueIdController(IGenerateUniqueIdUseCase getUseCase)
        {
            this._IdUseCase = getUseCase;
        }

        public string Get()
        {
            var request = new GenerateUniqueIdUseCaseRequest();
            var response = _IdUseCase.Execute(request);

            return response.Id;
        }
    }
}

「DI」プロジェクト内

DIRegister

/* using省略 */
namespace MyWebApp.DI
{
    public static class DIRegister
    {
        public static void Regist()
        {
            var container = new Container();
            container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

            container.Register<IAdminUserLoginUseCase, AdminUserLoginUseCaseInteractor>();
            container.Register<IGetCustomerLoginUseCase, GetCustomerLoginUseCaseInteractor>();

            /* 他、UseCaseの実体を注入 */

            container.Register<IGenerateUniqueIdUseCase, GenerateUniqueIdUseCaseInteractor>();

            container.Register<IAdminUserRepository, AdminUserRepository>();
            container.Register<ICustomerRepository, CustomerRepository>();
            // UniqueIdUseCaseで利用するRepositoryはCustomerRepositoryに組み込んでいるので書いていない。


            container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
            container.Verify();
            DependencyResolver.SetResolver(new SimpleInjectionDependencyResolver(container));
        }
    }
}

質問者の予想とやったこと

予想

SimpleInjectorのライフスタイルの設定がMVCとWebAPIで違うものを使うべきだと考えている。

やったこと

  • 別のライフスタイルを持つコンテナを二つ用意して注入したが、MVCで制作したWebページが例外で表示されなくなってしまった。
  • デフォルトライフスタイルを AsyncScopedLifestyle に変更したが、同じくMVCで制作したWebページが例外で表示されなくなってしまった。

以上です。
もしよろしければ回答いただけると幸いです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+2

最終的に実現されたいことは分かりませんが、少なくとも

System.ArgumentException が発生しました。
Message: 例外がスローされました: 'System.ArgumentException' (System.Core.dll の中)
追加情報:型 'MyWebApp.Controllers.GenerateUniqueIdController' には既定のコンストラクターがありません


に関しては、メッセージのとおり、GenerateUniqueIdControllerクラスに既定のコンストラクター、すなわち引数なしのコンストラクターpublic GenerateUniqueIdController()が定義されていないからではありませんか?

public GenerateUniqueIdController()
{
    // ※値がnullで良いかどうかは分かりません。
    this._IdUseCase = null
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/04 13:05

    たぶんそういうレベルの話では無い、と思いなおしました。大変失礼しました。

    キャンセル

  • 2019/10/06 18:58

    いえいえ。エラーメッセージの通りではあるものの、
    「既定のコンストラクター」が何を示しているのか理解できなかった僕には、エラーメッセージの意味をちゃんと理解できるきっかけになりました。ありがとうございます。

    キャンセル

  • 2019/10/06 19:06

    何か、デフォルトのコンストラクターを生成してしまうような、私の知らない魔法のようなファクトリーメソッドがASP.NET周りのフレームワーク内部にあり、それを前提としたご質問か、と思った次第です。

    キャンセル

checkベストアンサー

+1

注:下の【追記】の方が求められている回答に近いように思いますので、そちらも読んでください。

既定のコンストラクターがありません

それは多分 SimpleInjector とかとは全く関係なくて、基本的な文法違反の問題と思います。

質問者さんが定義した GenerateUniqueIdController には既定のコンストラクタ(引数がないコンストラクタ)がありません。

ブラウザが API を要求すると、Framework は GenerateUniqueIdController を初期化しようとしますが、その際には既定のコンストラクタ(引数がないコンストラクタ)を呼び出すはずですが、それが無いからエラーになっているのだと思います。

どういうことか、例を書いてもう少し詳しく言うと・・・

Visual Studio のテンプレートを使ってデフォルト設定で作る Web API プロジェクトの ValuesController.cs のコードは以下のようになっているはずです。

この場合 ValuesController にはコンストラクタは定義されてないので Framework が  ValuesController を初期化する際は継承元の ApiController を使用するので今回のような問題は起きません。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace WebApi2.Controllers
{
    [Authorize]
    public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        //・・・中略・・・
    }
}

ところが、例えば、上記のコードに以下のようなフィールドとコンストラクタを追加したとします。

private int myNumber;

public ValuesController(int i)
{
    this.myNumber = i;
}

そうすると、ValuesController には既定のコンストラクタ(引数がないコンストラクタ)が定義してないので、ブラウザから API 呼んだとき質問者さんのケースと同様なエラーになります。(質問者さんのケースで System.ArgumentException と違う理由は不明ですが)

イメージ説明

このエラーを解消するだけなら引数無しのコンストラクタ public ValuesController() {} を追加すれば良いです。ただ、それでは多分 SimpleInjector とかを使う目的は果たせないのではないかと思いますが。

【追記】

SimpleInjector とかは触ったこともなかったのですが、ちょっとググって調べてみました。

上のレスで、

ブラウザが API を要求すると、Framework は GenerateUniqueIdController を初期化しようとしますが、その際には既定のコンストラクタ(引数がないコンストラクタ)を呼び出すはずです

と書いたところは、以下の記事によると、DI コンテナが引数付きのコンストラクタに引数を渡して初期化するという動きになるそうです。

【連載】ASP.NET Web API を使おう:第4回 Simple Injector(DIコンテナ)を適用する
https://www.nuits.jp/entry/web-api-apply-simple-injector

自分でも、以下の画像の通り、Visual Studio Community 2015 の .NET 4.6.1 テンプレートでプロジェクトを作って、上の記事を参考に Web API 用の SimpleInjector の NuGet パッケージ「SimpleInjector.Integration.WebApi.WebHost.QuickStart」を追加して試してみました。

イメージ説明

上の記事と違うのが使ったテンプレートで、 MVC + Web API の両方が作られることと、ASP.NET Identity を利用した認証が実装されることです。

理由不明ですが DI コンテナは Controller のコンストラクタが 1 つだけでないと Controller を初期化できないようで、NuGet の適用で自動生成される SimpleInjectorWebApiInitializer クラスの container.Verify() メソッドでそれをチェックしているようです。(実際、上のテンプレートで自動生成される AccountController には 2 つコンストラクタが定義されていて、container.Verify() でエラーになりました)

とりあえずは認証は使わないので、AccountController の引数のある方のコンストラクタをコメントアウトすると container.Verify() は通って、下の画像のとおり Controller の初期化には成功します。

イメージ説明

結果、以下の画像の通り期待した応答が返ってきます。画面は MVC5 の Home/Index で、jQuery ajax を使って同一プロジェクト内の Web API に GET 要求を出し、応答を表示したものです。

イメージ説明

自動生成された SimpleInjectorWebApiInitializer のコードは以下の通りです。InitializeContainer メソッドの中だけは上に紹介した記事の通り手を加えています。

[assembly: WebActivator.PostApplicationStartMethod(typeof(WebApi2.App_Start.SimpleInjectorWebApiInitializer), "Initialize")]

namespace WebApi2.App_Start
{
    using System.Web.Http;
    using SimpleInjector;
    using SimpleInjector.Integration.WebApi;
    using SimpleInjector.Lifestyles;
    using WebApi2.Models;


    public static class SimpleInjectorWebApiInitializer
    {
        /// <summary>Initialize the container and register it as Web API Dependency Resolver.</summary>
        public static void Initialize()
        {
            var container = new Container();
            container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

            InitializeContainer(container);

            container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

            container.Verify();

            GlobalConfiguration.Configuration.DependencyResolver =
                new SimpleInjectorWebApiDependencyResolver(container);
        }

        private static void InitializeContainer(Container container)
        {
            container.Register<IEmployeeRepository, EmployeeRepository>(Lifestyle.Scoped);

            // For instance:
            // container.Register<IUserRepository, SqlUserRepository>(Lifestyle.Scoped);
        }
    }
}

ここまでは、Web API に SimpleInjector を追加しただけで、MVC に追加するにはどうすべきかというのが問題ですが、それについては以下の記事がありました。

Simple Injector initialize for both MVC and Web API controllers
https://stackoverflow.com/questions/37548510/simple-injector-initialize-for-both-mvc-and-web-api-controllers

チェックマークのついた回答のコメントに以下のように書かれています。

"we should use both .RegisterMvcControllers() and RegisterWebApiControlers() as well as both System.Web.Mvc.DependencyResolver(new SimpleInjectorDependencyResolver(container)) and GlobalConfiguration.Configuration.DependencyResolver(new SimpleInjectorWebApiDependencyResolver(container))"

質問者さんのケースで上記が解決策になるのかどうかは分かりませんが。

自分が作ったプロジェクトではどうなるかですが、今日は時間切れにつき、調べきれてません。(MVC 用の SimpleInjector の NuGet パッケージを追加して、上のコメントのようにしたらどうなるか、後で時間ができたらやってみようとは思っていますが)

質問者さんの方でも調べて情報提供いただければと思います。

なお、質問者さんの環境は「.netFramework 4」とのことですが、以下の記事に "Note: To be able to run the Web API integration packages, you need .NET 4.5 or above." とあるのが気になります。

ASP.NET Web API Integration Guide
https://simpleinjector.readthedocs.io/en/latest/webapiintegration.html

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/06 19:09

    私の代わりに調査までしてくださり、本当にありがとうございます。

    私個人つまづいていたところとして、
    「ApiControllerを継承したクラスなのにGetメソッドとコンストラクタを定義しただけでは動かないんだな」
    というところでした。

    その辺りの解説も含めてSimpleInjectorを使った実例と解説を示してくださったことに感謝の意を込めて、ベストアンサーとさせていただきます。

    キャンセル

  • 2019/10/07 16:20

    上の回答の【追記】で、MVC 用の SimpleInjector の NuGet パッケージを追加して、上のコメントのようにしたらどうなるか、後で時間ができたらやってみようとは思っていますと書きましたが、実際にやってみました。

    結果、MVC も Web API も SimpleInjector は動きました(動いたというだけで、詳細な検証はしていませんが)。手順は以下の通りです。

    (1) NuGet で SimpleInjector.Integration.Web.Mvc を追加。

    (2) 先に自動生成されている SimpleInjectorWebApiInitializer に以下を追加。(紹介した stackoverflow の記事の通り)

    container.RegisterMvcControllers(System.Reflection.Assembly.GetExecutingAssembly());

    DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

    (3) SimpleInjectorWebApiInitializer の

    container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

    を WebRequestLifestyle(); に変更。以下の記事に書いてある通り、MVC と Web API 混合プロジェクトでは WebRequestLifestyle を使うのが正解らしい。

    Async Scoped lifestyle vs. Web Request lifestyle
    https://simpleinjector.readthedocs.io/en/latest/lifetimes.html#asyncscoped-vs-webrequest

    キャンセル

  • 2019/10/23 16:41

    AccountController.cs の Controller にも DI できるようにしてみました。詳しくは以下の記事を見てください。

    SimpleInjector を ASP.NET MVC & Web API で利用
    http://surferonwww.info/BlogEngine/post/2019/10/19/simpleinjector-for-aspnet-mvc-with-web-api-project.aspx

    キャンセル

  • 2019/11/05 13:15

    返信が遅れて申し訳ございません。

    ブログ、拝読させていただきました!
    他のASP.netの記事もとても参考になると確信を覚えました。
    時間を作って擦り切れるくらいよく読みます。ありがとうございます!

    キャンセル

+1

Simple Injector については知らないのですが、
「Register generic types that have constructor arguments in Simple Injector」
https://stackoverflow.com/questions/36121585/register-generic-types-that-have-constructor-arguments-in-simple-injector
は参考にならないでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/06 18:54

    情報提供ありがとうございます。

    DbHelperというものがあったのですね。
    後々他のコンテナベースのDIツールにも通じそうなので、参考にさせていただきます。
    ありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる