🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
DI (Dependence Injection)

DI (Dependence Injection)は、「依存性の注入」という概念を指します。オブジェクト間で依存性のあるコードを外部の設定ファイルから注入するソフトウェアパターン設計思想です。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

ASP.NET Web API

ASP.NET Web APIはブラウザやモバイル機器のようなクライアント向けのHTTPサービスを構築するフレームワークです。Microsoft .NET Frameworkがベースになっており、RESTfulサービスを構築するには理想的です。

Q&A

解決済

3回答

2098閲覧

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

manzyun

総合スコア2244

DI (Dependence Injection)

DI (Dependence Injection)は、「依存性の注入」という概念を指します。オブジェクト間で依存性のあるコードを外部の設定ファイルから注入するソフトウェアパターン設計思想です。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

ASP.NET Web API

ASP.NET Web APIはブラウザやモバイル機器のようなクライアント向けのHTTPサービスを構築するフレームワークです。Microsoft .NET Frameworkがベースになっており、RESTfulサービスを構築するには理想的です。

0グッド

0クリップ

投稿2019/10/04 01:27

概要

タイトル通りです。

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

csharp

1/* using 省略 */ 2 3namespace MyWebApp 4{ 5 public class MvcApplication : HttpApplication 6 { 7 protected void Application_Start() 8 { 9 GlobalConfiguration.Configure(WebApiConfig.Register); 10 AreaRegistration.RegisterAllAreas(); 11 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 12 RouteConfig.RegisterRoutes(RouteTable.Routes); 13 BundleConfig.RegisterBundles(BundleTable.Bundles); 14 XmlConfigurator.Configure(new FileInfo(Server.MapPath("~/Web.config"))); 15 DIRegister.Regist(); 16 DapperModelInitializer.Initialize(); 17 } 18/* 以下略 */

Controller/GenerateUniqueIdController.cs

csharp

1/* using省略 */ 2 3namespace MyWebApp.Controllers 4{ 5 public class GenerateUniqueIdController : ApiController 6 { 7 private readonly IGenerateUniqueIdUseCase _IdUseCase; 8 9 public GenerateUniqueIdController(IGenerateUniqueIdUseCase getUseCase) 10 { 11 this._IdUseCase = getUseCase; 12 } 13 14 public string Get() 15 { 16 var request = new GenerateUniqueIdUseCaseRequest(); 17 var response = _IdUseCase.Execute(request); 18 19 return response.Id; 20 } 21 } 22}

「DI」プロジェクト内

DIRegister

csharp

1/* using省略 */ 2namespace MyWebApp.DI 3{ 4 public static class DIRegister 5 { 6 public static void Regist() 7 { 8 var container = new Container(); 9 container.Options.DefaultScopedLifestyle = new WebRequestLifestyle(); 10 11 container.Register<IAdminUserLoginUseCase, AdminUserLoginUseCaseInteractor>(); 12 container.Register<IGetCustomerLoginUseCase, GetCustomerLoginUseCaseInteractor>(); 13 14 /* 他、UseCaseの実体を注入 */ 15 16 container.Register<IGenerateUniqueIdUseCase, GenerateUniqueIdUseCaseInteractor>(); 17 18 container.Register<IAdminUserRepository, AdminUserRepository>(); 19 container.Register<ICustomerRepository, CustomerRepository>(); 20 // UniqueIdUseCaseで利用するRepositoryはCustomerRepositoryに組み込んでいるので書いていない。 21 22 23 container.RegisterMvcControllers(Assembly.GetExecutingAssembly()); 24 container.Verify(); 25 DependencyResolver.SetResolver(new SimpleInjectionDependencyResolver(container)); 26 } 27 } 28}

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

予想

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

やったこと

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

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

C#

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

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

C#

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

投稿2019/10/04 03:46

dodox86

総合スコア9256

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2019/10/04 04:05

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

2019/10/06 09:58

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

2019/10/06 10:06

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

0

ベストアンサー

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

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

それは多分 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/04 04:50

編集2019/10/06 06:39
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

manzyun

2019/10/06 10:09

私の代わりに調査までしてくださり、本当にありがとうございます。 私個人つまづいていたところとして、 「ApiControllerを継承したクラスなのにGetメソッドとコンストラクタを定義しただけでは動かないんだな」 というところでした。 その辺りの解説も含めてSimpleInjectorを使った実例と解説を示してくださったことに感謝の意を込めて、ベストアンサーとさせていただきます。
退会済みユーザー

退会済みユーザー

2019/10/07 07: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
manzyun

2019/11/05 04:15

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

0

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/04 03:53

KOZ6.0

総合スコア2707

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

manzyun

2019/10/06 09:54

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問