2週間前に 新しいASP.NETのMVC(モデルビューコントローラー)フレームワークについてブログを投稿しましたが、間もなくオプションの機能としてサポートする予定です。それにより構造化されたモデルが提供され、アプリケーション内での問題の適切な分別化を強化し、コードの単体テストを簡易化し、TDDワークフローをサポートします。また、アプリケーションで公開したURLをより制御できるようにし、オプションとしてそこから省略されたHTMLの制御もできるようになります。
あれから、これについてかなりの質問に対してお答えしてきました。この興味レベルから考えると、使用方法の詳細が書かれたブログ投稿をまとめたほうがいいと思いました。この最初の投稿は、今後数週間で行う予定のものです。
単純なEコマース・ストアフロント・アプリケーション
ASP.NET MVC フレームワークの動作をご紹介するために単純なネット商店のアプリケーションを使用します。今日の投稿では、その中で製品の一覧化と検索を行うシナリオを実行しようと思います。
特に、ユーザがサイト上の/Products/Categories URLを訪れた際に製品カテゴリ一覧を検索する商店のフロントを構築していきます。:
上記のページ上でユーザが製品カテゴリリンクをクリックした時、製品カテゴリ一覧 URL - /Products/List/CategoryName - へ導かれ、そこから特定のカテゴリの利用可能な製品を一覧化します。:
ユーザが各製品をクリックした時、製品の詳細 URL - /Products/Detail/ProductID - へ導かれ、選択された特定の製品についての詳細が表示されます。:
新しいASP.NET MVC フレームワークを使用して上記の機能を全て構築します。これはアプリケーションの異なるコンポーネント間で”問題の適切な分別化”の保持を可能にし、単体テストやテスト駆動型の開発の統合をより簡単に可能にします。
新しい ASP.NET MVC アプリケーションの作成
ASP.NET MVCフレームワークには Visual Studio プロジェクト・テンプレートが含まれており、新しいWebアプリケーションを簡単に作成することができます。単純にファイル-新しいプロジェクトをメニューから、”ASP.NET MVC Webアプリケーション”テンプレートを選択し、それを使用して新しいWeb アプリケーションを作成します。
デフォルトでは、このオプションを使用して新しいアプリケーションを作成する際、Visual Studioは新しいソリューションを作成し、2つのプロジェクトをそこへ追加します。最初のプロジェクトはWebプロジェクトで、アプリケーションを実装します。2つ目はテスト用プロジェクトで、単体テストを書くために使用することができます。:
ASP.NET MVCフレームワークで全ての単体テストフレームワーク(NUnit, MBUnit, MSTest, XUnit, など) を使用することができます。VS 2008 プロフェッショナルではMSTest(以前VS2005ではVisual Studioチームシステム SKUが必要でした。)に対するサポートを行うビルドインのテスト用プロジェクトが含まれており、またデフォルトのASP.NET MVCプロジェクトテンプレートは自動的にVS2008を使用した時にこれらのプロジェクトを作成します。
また、NUnitやMBUnit、またその他の単体テストフレームワークに対するプロジェクトテンプレートのダウンロードも出荷しますので、もし代わりにこれらを使用したい場合は、アプリケーション作成に簡単ワンクリック方法が提供され、それを使って使用できるテストプロジェクトが用意されます。
プロジェクトのフォルダ構成を理解
ASP.NET MVCアプリケーションのデフォルトのディレクトリ構造には3つのトップレベルディレクトリがあります。:
- /Controllers
- /Models
- /Views
ご想像の通り、Controllerクラスは/Controllersディレクトリの下、データモデルクラスは /Modelsディレクトリの下、ビューテンプレートは /Views ディレクトリの下へ置くことをお勧めします。
ASP.NET MVC フレームワークはこの構造の使用を常に強制しないですが、デフォルトプロジェクトテンプレートはこのパターンを使用しておりアプリケーションを構造化する簡単な方法としてお勧めしています。ほかのファイルレイアウトを使用する特別な理由がない限り、このデフォルトパターンをお勧めします。
コントローラクラスへURLをマッピング
ほとんどのWebフレームワーク(ASP, PHP, JSP, ASP.NET WebFormsなど)では、入ってくるURLは通常ディスク上に保存されたテンプレートファイルへマップされます。たとえば、"/Products.aspx"や"/Products.php"URLは通常ディスク上のProducts.aspxやProducts.phpテンプレートファイルがあり、それを処理します。Webアプリケーションに対するHTTPリクエストがサーバに入って来た時は、Webフレームワークはディスク上のテンプレートファイルによって指定されたコードを起動し、このリクエストの処理をこのコードが扱います。大体においてこのコードはHTMLマークアップを、Products.aspxやProducts.phpファイル内で使用して、クライアントに返すリスポンスを生成します。
MVC フレームワークは通常URLをサーバーのコードへ異なる方法でマップします。ディスク上のテンプレートファイルへURLをマッピングする代わりに、クラスに直接URLをマップします。これらのクラスは"コントローラ"と呼ばれ、入ってくるリクエストの処理や、ユーザの入力の処理や、適切なアプリケーションやそれらに基づいたデータロジックの実行を担当します。その後コントローラクラスは別の"ビュー"コンポーネントを呼び出し、それがリクエストに対する実際のHTML出力の生成を担当します。
ASP.NET MVC フレームワークは非常に強力なURLマッピングエンジンをもっており、URLをコントローラクラスへマップする方法に大いな柔軟性を提供しています。簡単なセットアップルーティングルールを使用することができ、それによりASP.NETは入ってくるURLを評価し、実行するコントローラを選択します。また、自動的に変数をパースするルーティングエンジンを持つことができ、それはURL内で定義しASP.NETが自動的にそれらを引数としてコントローラへ引き渡します。このシリーズの今後のブログ投稿でURLのルーティングエンジンを利用したより上級のシナリオをカバーしています。
コントローラクラスへデフォルトの ASP.NET MVC URLのルーティング
デフォルトで、ASP.NET MVC プロジェクトは事前に構成されたURLルーティングルールセットがあり、特別に何かを構成する必要なくアプリケーション上で開始することが可能です。代わりにデフォルトセットの名前ベースのURLマッピング変換を使用してコーディングを開始することができます。これは、Visual Studioの新しいASP.NET MVCプロジェクトテンプレートにより作成されたGlobal.asaxのASP.NETアプリケーションクラス内で宣言されます。
デフォルトの名前変換は入ってくるHTTPリクエスト(例えば、 /Products/)の最初のURLパスを次のパターンに合致した名前を持つクラスへマップします。 URLパスコントローラ (例えば、デフォルトだと最初に /Products/で始まっているURLは Productsコントローラという名前のクラスにマップされます。)
電子取引製品を検索する機能を構築するには、新しい"ProductsCotnroller"クラスをプロジェクトに追加します。(Visual Studioでは"新しい項目の追加"を使用して簡単にコントローラクラスをテンプレートから作成することができます。):
ProductsControllerクラスはSystem.Web.MVC.Controllerベースクラスから派生しています。このベースクラスからの派生は必要ではありませんが、いくつかの有用なヘルパーメソッドや機能が含まれており、それらはあとで利用したくなるものだと思います。:
一度このProductsControllerクラスをプロジェクト内で定義すると、ASP.NET MVCフレームワークはデフォルトでそれを使用して入ってくる全ての"/Products/"の配下から始まるアプリケーションURLを処理します。 つまり、それは、商店のフロントアプリケーションで有効にする予定の"/Products/Categories"、 "/Products/List/Beverages"、 "/Products/Detail/3" URLを処理するために自動的に呼ばれます。
今後のブログ投稿で、(エンドユーザがショッピングカートを管理できるようにする)ShoppingCartControllerとAccountController (エンドユーザがサイト上で新しいメンバーアカウントを作成し、そのログインとログアウトができるようにする)も追加します。これら2つの新しいコントローラクラスをプロジェクトに追加すると、 /ShoppingCart/ と /Account/ から始まるURLは自動的に処理のためにそれらをルーティングさせます。
注: ASP.NET MVC フレームワークで常にこの名前付け変換パターンを使用しなければいけないわけではありません。デフォルトでアプリケーションがこれを使用する理由は、これがVisual Studioを使用して新しいASP.NET MVCプロジェクトを作成したときにASP.NETアプリケーションクラスへ自動的に追加されており、これを構成するマッピングルールがあるためです。もしこのルールが嫌だったり、異なるURLマッピングパターンを使用してカスタマイズしたい場合などは、(Global.asaxの中の)ASP.NETアプリケーションクラスにいって、それを変更するだけです。このやり方については今後のブログ投稿でカバーします。(その時、URLのルーティングエンジンが可能にするシナリオもいくつか紹介しようと思います。)
コントローラアクションメソッドの理解
プロジェクトにProductsControllerクラスをが作成されたので、アプリケーションに入ってくる"/Products/" URLの処理を取り扱うロジックを追加していきます。
このブログの初めで、電子取引のフロント定義がケースを使用した時、サイト上で3つのシナリオを実装すると言いました。: 1) Productカテゴリを全て検索、 2) 特定のカテゴリ内のProductを一覧化、3) 特定のProductについて詳細を表示。 次のSEOフレンドリなURLを使ってこれらの各シナリオを処理していきます。:
| URL フォーマット | 動作 | URL 例 |
| /Products/Categories | 全てのProduct Categoriesを検索 | /Products/Categories |
| /Products/List/Category | Category内のProductsを一覧化 | /Products/List/Beverages |
| /Products/Detail/ProductID | 特定のProductの詳細を表示 | /Products/Detail/34 |
入ってくるこれら3つのタイプのURLを処理するProductsController クラス内でコードを書く方法が2つほどあります。1つは、コントローラベースクラスで"Execute"メソッドをオーバーライドして、入ってくるリクエストされたURLを見るためのif/elseスイッチするロジックを書いて、それを処理するための適切なロジックを実行します。
もっと簡単なアプローチは、MVCフレームワークのビルドインの機能を使うことです。それは、コントローラ上で”アクションメソッド”を定義することができ、アプリケーションで使用されるURLルーティングルールに基づいて、コントローラベースクラスに自動的に適切なアクションメソッド実行させます。
例えば、以下の3つのコントローラアクションメソッドをProductsController クラスに追加して上記の3つの電子取引URLシナリオを処理します。:
新しいプロジェクトが作成された時にデフォルトで構成されたURLルーティングルールは、リクエストのアクション名としてコントローラ名に続くURLのサブパスを取り扱います。そのため、私たちは/Producs/CategoriesのURLリクエストを受取り、ルーティングルールはアクション名として"Categories"を取り扱い、Categories()メソッドはリクエストを処理するために起動されます。もし/Products/Detail/5のURLリクエストを受け取ると、ルーティングルールはアクション名として"Detail"を取り扱い、Detail()メソッドがリクエストの処理にために起動されます。
注: ASP.NET MVC フレームワークでこのアクション名前付け変換パターンを常に使用しなければならないということではありません。もし異なるURLマッピングパターンを使用したい場合は、ASP.NETアプリケーションクラス(Global.asax内)に行き、それを変更してください。
コントローラアクションメソッドへURLパラメータをマッピング
コントローラクラスのアクションメソッド内でURLパラメータ値にアクセスする方法はいくつかあります。
コントローラのベースクラスは使用されるRequestとResponseオブジェクトのセットを公開しています。これらのオブジェクトはASP.NETで既によくご存じのHttpRequest/HttpResponseオブジェクトと全く同じ構造を持っています。重要な違いはこれらのオブジェクトはシールクラスの代わりにインターフェイスベースのオブジェクトであることです。(特に、MVCフレームワークは新しいSystem.Web.IHttpRequest と System.Web.IHttpResponseインターフェイス と共に出荷されます。)これらをインターフェイスにする利点はそれらを簡単に模倣できるようになることです。それにより、コントローラクラスの単体テストが簡単にできるようになります。これは今後深くカバーしていきます。
以下はProductsControllerクラスのDetailアクションメソッド内からIDクエリストリングの値を手動で取得するためのRequest APIを使用する例です。:
ASP.NET MVCフレームワークはまた自動的に入ってくるURLパラメータ値をアクションメソッドへの引数としてマッピングするサポートも行います。デフォルトで、もしアクションメソッド上で引数をもっているとしたら、MVCフレームワークは入ってくるリクエストデータを見て、対応しているHTTPリクエスト値が同じ名前であるかどうかを確認します。もしあれば、自動的にそれをアクションメソッドへパラメータとして引き渡します。
例えば、Detailアクションメソッドを書きなおしてこのサポートを活用し、以下のようによりクリーンにすることができます。:
リクエストのクエリストリング/フォームコレクションから引数の値をマッピングすることに加え、ASP.NET MVCフレームワークはまたMVC URLのルートマッピングインフラを使用してコアのURL自身にパラメータ値を埋め込むことができます。(例えば: /Products/Detail?id=3の代わりに、/Products/Detail/3を使用することができます。)
新しいMVC プロジェクトを作成した時に宣言されたデフォルトのルートマッピングルールは次のフォーマットになります。: "/[controller]/[action]/[id]"。つまり、URLにコントローラとアクション名の後に何かURLのサブパスがあった場合、デフォルトで"id"という名前のパラメータとして取り扱われ、それは自動的にコントローラアクションメソッドにメソッドの引数として引き渡されます。
要するに、Detailメソッドを使用してID引数をURLパスから取得して処理することもできます。(すなわち、 /Products/Detail/3):
同様のアプローチをListアクションに対して使用すれば、カテゴリ名にURLの一部として引き渡すことができます。(例えば、/Products/List/Beverages)コードをより読みやすくするに、ルーティングルールを微調整しました。そうすることで、"id"と呼ばれる引数の代わりにこのアクションに対して"category"という名前になります。
以下は全てのURLのルーティングおよびパラメータマッピングサポートが実装されたバージョンのProductsControllerクラスです。:
上記でListアクションがどのようにURLの一部としてカテゴリパラメータをとり、クエリストリングとしてオプションのページインデックスをとっているかを確認してください(サーバーサイドページングを実装し、どのページのカテゴリデータをこのリクエストで表示させるかを示す値を使用します。)
MVCフレームワークのオプションパラメータはコントローラアクションメソッド上でNullableタイプの引数を使用して取り扱われます。これは、Listアクション上のページパラメータはNullable int(これは構文的に"int?"の意味になります。)で、MVCフレームワークはもしURLに表示されていれば値に引き渡され、もしなければNullに引き渡されます。 以前の ?? Null合体演算子をご確認いただき、このように引数として渡されるNullable型と動作させるための有用なヒントや仕掛けを習得してください。
データモデルオブジェクトを構築
現在ProductsControllerクラスと3つのアクションメソッドがあるため、入ってくるWebリクエストを処理できるようになりました。次のステップは、いくつかのクラスを構築して、これらのWebリクエストを処理するのに必要な適切なデータを取得するためにデータベースと動作させるようにします。
MVCの世界では、"モデル"はアプリケーションのコンポーネントで、状態を保持する役割をします。Webアプリケーションでは、この状態は通常データベースの内部で保持されます。(例えば、ProductオブジェクトはSQLデータベースの内部のProductsテーブルからの製品データを表すために使用されます。)
ASP.NET MVC フレームワークでは、モデルの取得や管理をするために必要なデータアクセスパターンまたはフレームワークを使用することができます。もしADO.NETのDataSetやDataReader(またはそれらの上に構築された抽象型を)使用したい場合はそれらを使用することができます。もしNHibernate、LLBLGen、WilsonORMapper、LINQ to SQL/LINQ to Entitiesのようなオブジェクトリレーショナルマッパー(ORM)を使用したい場合も、当然使用することができます。
電子取引サンプルアプリケーションには、.NET 3.5 およびVS 2008で出荷されたビルドインのLINQ to SQL ORMを使用します。これをカバーした現在進行中のブログのチュートリアルシリーズからLINQ to SQLについて習得することができます。(特にパート1, パート2, パート3 およびパート4 の投稿をご確認ください。)
まず、VSにあるMVCのWebプロジェクトの"Models"サブディレクトリ上を右クリックし、”新しい項目を追加”オプションを選択し、LINQ to SQLモデルを追加してください。LINQ to SQL ORMデザイナ内で、SQLサーバのNorthwindサンプルデータベースにあるCategories、Products、Suppliersテーブル3つのデータモデルクラスを定義します。(この方法については、LINQ to SQL シリーズのパート2 を読んでください。):
LINQ to SQLデータモデルクラスを定義した後、新しいNorthwindDataContext のpartial クラスもModelsディレクトリに追加します。:
このクラスの中で、いくつかのLINQ表現をカプセル化するいくつかのヘルパメソッドを定義し、それによりデータベースからユニークなCategoryオブジェクトを取得するために使用し、データベース内の特定のカテゴリにあるすべてのProductオブジェクトを取得、また提供されたProductIDに基づいて個々のProductオブジェクトも取得します。:
これらのヘルパメソッドは、(コントローラクラス自身にLINQ文を書く必要なく)ProductsController クラスから必要なデータモデルオブジェクトをクリーンに取得できるようにします。:
ProductsController の機能を実装を完成させるのに必要な全てのデータコード・オブジェクトが揃いました。
ProductsControllerクラスの実装の完成
MVCベースのアプリケーションにあるコントローラは入ってくるリクエストの処理や、ユーザの入力の処理、それらに基づいた適切なアプリケーションロジックの実行(データベースに保存されたモデルデータの取得および更新など)を行う役割があります。
コントローラは通常リクエストに対して特別なHTMLの生成を行いません。HTMLリスポンスを生成するタスクはアプリケーション内の"View"コンポーネントが行います。これは、コントローラとは別のクラスやテンプレートとして実装されます。Viewはプレゼンテーションロジックをカプセル化することだけに焦点を当てようとし、ほかのアプリケーションロジックやデータベース取得コード(代わりにコントローラが全てのアプリケーションロジックを処理します。)は含まれるべきではありません。
典型的なMVC Webワークフローでは、コントローラアクションメソッドは入ってくるWebリクエストを処理し、入ってくるパラメータ値を使用して適切なアプリケーションロジックコードを実行し、データベースからのデータモデルオブジェクトを取得または更新し、そして"View"を選択してブラウザに返す適切なUIリスポンスを描画します。描画するために適切なViewを選ぶ一部として、コントローラは適切なリスポンスを描画させるために"View"が必要としているすべてのデータや変数を(引数として)明示的に引き渡します。:
このようにコントローラとViewを分別する利点は何なのか?何故同じクラスに一緒に置かないのか?など疑問に思われているかもしれません。このようにアプリケーションを分ける一番の目的はUI生成コードからアプリケーションおよびデータロジックを分別させるためです。これは、UI描画ロジックからアプリケーションおよびデータロジックを分別することで、単体テストがより簡単になるのです。また、これは、ビューテンプレート内にアプリケーションおよびデータロジックを事故的に追加することが難しいため、アプリケーションのメンテナンスをしやすくすることができます。
ProductsController クラスの3つのコントローラアクションメソッドを実装した時、入ってくるURLパラメータ値を使用して、適切なモデルオブジェクトをデータベースから取得し、その後"View"コンポーネントをを選択して適切なHTMLリスポンスを描画するために使用します。使用したいViewを特定するため、またはリスポンスを描画するのにViewに使用させたい特定のデータへ明示的に引き渡すために、コントローラのベースクラス上のRenderView()メソッドの1つを使用します。
以下はProductsController 実装の最終結果です。:
上記のアクションメソッド内のコードの行数をみると、非常に少ない(2行ずつ)です。この理由の一部として、URLパラメータのパーシングロジックがMVCフレームワーク(このコードの多くを書かなくてすみます。)により全体的に処理されるからです。また別の理由として、製品を検索するシナリオがビジネスロジック的な観点から非常にシンプルなものだからです。(アクションメソッドはすべて読取専用の表示シナリオです。)
一般的にですが、たまに"細いコントローラ"と呼ばれる(比較的簡潔、つまり10行以下のコードのアクションメソッドによるコントローラメソッド)ものがあることに気づかれると思います。これは、データロジックがクリーンにカプセル化され、コントローラロジックが上手く要素化されているという良い印です。
ProductsControllerの単体テスト
次に作業するステップに驚かれるかもしれませんが、アプリケーションロジックと機能をテストします。これは可能なのか?と疑問に思うかもしれません。私たちはまだViewを実装していないし、アプリケーションは現在HTMLを1つのタグも描画していません。これはMVCアプローチが魅力的になる理由でもあり、ViewやHTML生成ロジックと全く別にアプリケーションのコントローラやモデルロジックを単体テストすることができます。以下でお分かりのように、Viewを作成する前でも単体テストは可能なのです。
現在作業しているProductsController クラスを単体テストするためには、ProductsControllerTestクラスを、Visual Studioを使用してASP.NET MVCアプリケーションを作成した時にデフォルトでソリューションに追加されたテストプロジェクトに追加します。:
ProductsControllerのDetailアクションをテストする簡単な単体テストを定義します。:
ASP.NET MVCフレームワークは特に簡単な単体テストを有効にするために設計されました。フレームワーク内のすべてのコアなAPIとコントラクトはインターフェイスで、拡張性のあるポイントはオブジェクトの簡単なインジェクションとカスタマイズ(Windsor、StructureMap、Spring.NET、ObjectBuilderなどのIOCコンテナを使用する機能を含む)を可能にするために提供されています。開発者はビルドインのモッククラスや他の.NET型のモッキングフレームワークを使用して、MVC関係のオブジェクトの各テストバージョンをシミュレートすることができます。
上記の単体テストでは、Detail()アクションメソッドを呼び出す前にダミーの”ViewFactory”実装をProductsController クラス上に挿入している例をご確認頂けると思います。このようにすることで、デフォルトのViewFactoryをオーバーライド(しない場合はViewを作成・描画の処理を行います。)しています。このテストViewFactory実装を使用して、ProductsController のDetailアクション動作のテストを隔離します。(これを行う実際のViewを呼び出す必要がありません。)その後に3つのAssertステートメントをDetailアクションメソッドが呼ばれた後に使用して、その中で起こる正しい動作を検証します。(特に正しいProductオブジェクトを取得し、適切なViewへそれを引き渡したアクション。)
なぜなら、MVCフレームワーク(IHttpRequest および IHttpResponseオブジェクトを含む)ではどんなオブジェクトもモックしシミュレートすることができ、実際のWebサーバのコンテキストで単体テストを走らせる必要がないからです。代わりに、通常のクラスライブラリ内でProductsController を作成することができ、直接それをテストすることができます。これは単体テストの実行速度を大きく早めることができ、またそれらの構成や機動を単純化することができます。
もしVisual Studio 2008 IDEを使用している場合は、簡単にテスト起動の成功・失敗を追跡することができます。
(この機能は現在VS 2008 Professionalにビルドインされています。):
ASP.NET MVCフレームワークにより簡単にテストを書くことができ、上手いTDDワークフローを有効にさせることがお分かり頂けると思います。
ViewでUIを描画
この電子取引アプリケーションの製品検索セクションに対するアプリケーション+データロジックの実装とテストが終了しました。次にHTMLのUIを実装する必要があります。
RenderView() メソッドを呼び出す時にProductsController アクションメソッドが提供されたビュー関連のデータオブジェクトを使用して適切なUIを描画する"View"を実装することでこれを行います。:
上記のコード例では、RenderViewメソッドの"Categories"パラメータは描画しようとするビュー名を示しており、2つ目の引数はカテゴリオブジェクトの一覧で、それをビューへ引き渡し、適切なHTMLのUIを生成するためのデータとして使用されるようにします。
ASP.NET MVCフレームワークはUIを生成するためのテンプレートエンジンを使用することができます。(NVelocity、Brailや、または自分で書いた新しいものなどの既存のテンプレートエンジンなどを含む。)デフォルトではASP.NET MVCフレームワークは既存のASP.NETページ(.aspx)、マスターページ(.master)、ユーザコントロール(.ascx)サポートを使用します。
ビルドインのASP.NETビューエンジンを使用して電子取引アプリケーションのUIを実装します。
Site.Master ファイルを定義
このサイトに多くのページを構築しようとしているため、サイトの中で共通のHTMLレイアウトやクロームをカプセル化して使用することができるマスターページをまず定義することでUIの作業を開始します。"Site.Master"と呼ばれるファイルの中でこれを行います。これはプロジェクトの\Views\Sharedディレクトリの下に作成されます。:
サイトに対してすべてのスタイルをカプセル化するために外部のCSSスタイルシートを参照し、マスターページを使用してサイトのすべてのレイアウトを定義し、またページに特定のコンテンツを埋めることができるページを置くコンテンツのプレースホルダーリジョンを確認します。オプションとしてすべてのこの新しいVS2008デザイン機能をこれを行うことで使用することができます。 (HTML 分割ビューデザイナ,、CSS編集、 ネスト化されたマスターページのサポートを含む。)
/Views ディレクトリ構造を理解
デフォルトではASP.NET MVCプロジェクトをVisual Studioを使用して作成する時、"Shared"サブディレクトリが"Views"ディレクトリルートの下に作成されます。これはアプリケーション内で複数のコントローラで共有したいマスターページ、ユーザコントロール、ビューを保存する推奨の場所です。
各コントローラに特定のビューを構築する際、デフォルトのASP.NET MVC変換でそれらを\Viewsルートの下のサブディレクトリに保存します。デフォルトでサブディレクトリの名前はコントローラの名前と対応しています。例えば、構築しているコントローラクラスは"ProductsController"と呼ばれており、\Views\Productsのサブディレクトリ内にそれに特定のViewsをデフォルトで保存します。:
特定のコントローラ内でRenderView(string viewName)メソッドを呼び出すとき、MVCフレームワークは自動的にまず\Views\コントローラ名ディレクトリの下で呼応する.aspxおよび.ascxビューテンプレートを探しに行き、その後もし適切なビューテンプレートがなければ、\Views\Sharedディレクトリをチェックします。:
Categories のViewを作成
"新しい項目を追加"メニューオプションをProductsディレクトリ上で使用し、"MVCビューページ"項目テンプレートを選択することで、Visual Studio内でProductsController に対する"Categories"ビューを作成することができます。これは新しい.aspxページを作成します。これはオプションとしてサイトのすべてのルックアンドフィールを選択するためにSite.Masterマスターページと関連付けることができます。(マスターページのようにWYSIWYGデザイナサポートを全て得ることができます。)
:
MVCパターンを使用してアプリケーションを構築する時、できるだけViewコードを単純にして、UIを描画するだけにします。アプリケーションとデータの取得ロジックはコントローラクラスの内部でのみ書かれているべきです。そうすると、コントローラクラスはRenderViewメソッドが呼ばれた時にビューを描画するのに必要なデータオブジェクトへ引き渡すかどうかを選択することができます。例えば、以下のProductsController クラスのCategoriesアクションメソッドで、CategoriesビューへCategoryオブジェクトのListコレクションを引き渡しています。:
デフォルトではMVC ビューページはSystem.Web.Mvc.ViewPageのベースクラスから派生し、多くのMVC特定のヘルパメソッドやプロパティを提供しており、UIを構築するために使用することができます。これらのViewPageプロパティは"ViewData"という名前で、コントローラがRenderView() メソッドへの引数として渡すビュー特有のデータオブジェクトへアクセスを提供しています。
ビューの中から、遅延バインドまたは強く型付けされた方法のどちらででも"ViewData"へアクセスすることができます。もしビューがViewPageから派生していたら、ViewDataプロパティは遅延バインドディレクトリとして型づけされます。もしビューがViewPage<T> ベースのジェネリックから派生していたら(TがViewへコントローラが引き渡すViewDataのデータオブジェクト型を示している。)、ViewDataプロパティはコントローラに渡された同じ型と一致するように強く型付けされます。
例えば、以下のCategoriesビューのコードビハインドクラスはViewPage<T> から派生しています。(TはCategoryオブジェクトのListとして示しています。)
:
これは、List<Category> ViewDataを提供するProductsController.Categories()に対して作業する時、Viewコード内で型の安全性、インテリセンス、コンパイル時のチェックを全て得ることができることを意味しています。:
Categories Viewの描画:
もしこの投稿の最初の方にあったスクリーンショットにあったように、Categories ビューで製品カテゴリの一覧を表示したいと思います。:
このHTML UIの生成コードをCategories View実装内で2つの内1つの方法で書くことができます。: 1).aspxファイル内でインラインコードを使用。2).aspxファイル内でサーバーコントロールを使用しコードビハインドからデータバイディング
描画アプローチ1:インラインコードの使用
ASP.NET ページ、ユーザコントロール、マスターページは<% %>や<%= %>シンタックスをサポートしており、HTMLマークアップ内で描画するコードを埋め込みます。このテクニックをCategoriesビュー内で使用して、HTMLカテゴリ一覧を生成するforeachループを簡単に書くことができます。:
VS 2008はVBおよびC#の両方に対してソースエディタ内でコードインテリセンスを全て提供しています。つまり、ビューへ引き渡されたCategoryモデルオブジェクトに対してインテリセンスを得ることができるのです。:
VS 2008はまたインラインコードに対してデバッガサポートを全て提供しています。(デバッガでViewにブレークポイントの設定および動的な検証を行うことができます。):
描画アプローチ2: サーバサイドコントロールの使用
ASP.NET ページ、ユーザコントロール、マスターページは、またHTML UI生成をカプセル化するために宣言的なサーバコントロールを使用することができます。上記のようにインラインコードを使用する代わりに、
.NET 3.5の新しい <asp:listview>コントロール を使用することができます。:
上記でListViewコントロールが値の一覧を描画、また一覧に項目が無い場合の処理の両方をカプセル化している様子がお分かり頂けると思います。その後、カテゴリオブジェクトの一覧をリストビューコントロールに以下のようなコードビハインドコードを使用してデータバインドすることができます。
:
重要: MVCの世界では、Viewのコードビハインドクラス(他のアプリケーションやデータロジックではなく)で描画ロジックだけを置きたいと思います。上記でお分かりのように持っている唯一のロジックは強く型づけされたListViewコントロールに強く型付けされたCategoryオブジェクトのViewDataコレクション割り当てるものです。ProductsController コントローラクラスはViewではなく実際にデータベースからCategoriesの一覧を取得するものです。
このビューテンプレートのListViewサーバコントロールバージョンはその後インラインコードバージョンと全く同じのHTMLを生成します。ページ上に<form runat="server">コントロールを持っていないため、ViewState、ID値、その他のマークアップが省略されません。純粋なCSSフレンドりHTMLだけです。:
Html.ActionLink メソッド
上記のViewコードスニペットのインラインコードとサーバコントロールバージョンで気づかれることの1つがHtml.ActionLink メソッドへの呼び出しだと思います。:
HTMLオブジェクはViewPageベースクラス上のヘルパプロパティで、ActionLinkメソッドはヘルパで動的にHTMLのハイパーリンクを簡単に生成できるようにしコントローラ上でアクションメソッドにリンクバックします。もし上記のセクションでHTMLの出力画像を見ていたら、このメソッドによって生成されたHTMLの出力例をいくつか確認できると思います。
:
<a href="http://weblogs.asp.net/Products/List/Beverages">Beverages</a>
私が使用しているHtml.ActionLinkヘルパメソッドのシグネチャーはこのようになります。:
string ActionLink(string text, object values);
最初の引数は描画するハイパーリンクの内部コンテンツを表しています。(例えば、<a>テキストはここ</a>へ行きます。)2つ目の引数は、匿名型オブジェクトで、実際のURLを生成させるために使用する一連の値を表しています。 (これを 辞書を生成するクリーンな方法として考えることができます。) URLルーティングエンジンをカバーする今後のブログ投稿でこれがどのように動作するのかより詳細に入っていきたいと思います。簡単にまとめると、URLルーティングシステムを使用して入ってくるURLの処理と、出力するHTMLで省略することができるURLを生成することができます。もしこのようなルーティングルールを持っているとしたら:
/<controller>/<action>/<category>
そのときはProductControllerのCategoryビュー内でこのコードを書きます。:
<%= Html.ActionLink("クリックしてBeveragesを確認してください。", new { action="List", category="Beverages" } %>
ActionLink メソッドはアプリケーションのURLマッピングルールを使用してパラメータで交換し、この出力を生成します。:
<a href="http://weblogs.asp.net/Products/List/Beverages">クリックしてBeverages</a>を見てください。
これはアプリケーション内で簡単にURLおよびAJAXコールバックをコントローラに生成することができることを意味しています。また、URLルーティングルールを1つの場所で更新し、アプリケーション全体で入ってくるURLの処理と出ていくURL生成の両方に対する変更を自動的にコードに拾わせることができます。
重要: テスト性を強化するために、MVCフレームワークはView内でサーバーコントロールへの直接のポストバックイベントをサポートしていません。その代わりに、ASP.NET MVCアプリケーションはコントローラのアクションにハイパーリンクとAJAXコールバックを生成し、その後、出力を描画するためにViews(およびそれらの中にあるその他のサーバコントロール)だけを使用します。これにより、Viewロジックは最初に抑えられ、描画のみに焦点が当たるようになるため、簡単にコントローラクラスを単体テストし、すべてのアプリケーションやデータロジックの動作をViewとは離れて検証することができます。この詳細については今後ブログにしたいと思います。
まとめ
今回の最初のブログ投稿は非常に長くなりましたが、新しいASP.NET MVCフレームワークの全ての異なるコンポーネントが全て共にフィットしている様子と、それとともに共通の実際の世界のシナリオを構築することができる様子を広い視野で提供できていれば幸いです。最初のASP.NET MVCビッツの公開プレビューは数週間以内に利用可能となり、それを使用することで上記で概要をまとめたステップを全て行うことができます。
MVC(特に問題の分別化の概念)固有の概念の多くはおそらくこれを読んでいる人達にとって新しいものであると思いますが、このブログ投稿でASP.NET MVC実装が既存のASP.NET、.NET、Visual Studioの機能セットへ非常にクリーンにフィットしていることをご紹介できていれば幸いです。.ASPX、.ASCX、.MASTERファイル、ASP.NET AJAXを使用してASP.NET MVCビューを作成することができます。今日ASP.NETでUI機能ではない、Forms Authentication, Windows Authentication, Membership, Roles, Url Authorization, Caching, Session State, Profiles, Health Monitoring, Configuration, Compilation, Localization, HttpModules/HttpHandlersでもすべてMVCモデルを全てサポートしています。
もしMVCモデルが嫌いだったり、開発スタイルと合わない場合は、使用する必要は全くありません。これは完全にオプションで、既存のWebFormsページコントローラモデルを差し替えるものではありません。WebFormsもMVCも両方サポートされ、前へと前進しています。1つのアプリケーションの構築も可能で、その中にWebFormsを使用して書かれたパーツ、およびMVCアプローチを使用して書かれたパーツをお好みで持つことができます。
上記のMVC投稿をお気に入りいただけた場合(もしくは興味を持ってさらに詳細を知りたい場合)、今後のブログにご期待ください。MVC概念についてカバーし、電子取引アプリケーションの構築に使用してその機能をもっとご紹介します。
Hope this helps,
Scott