ScottGu's blog translated by Chica @ Wankuma

ASP.NET MVC フレームワーク (パート 4): フォーム編集と送信シナリオの処理

  

ここ数週間、現在取りかかっている新しいASP.NET MVCフレームワークをカバーするブログ投稿のシリーズを行っています。ASP.NET MVC フレームワークはオプションとしてのアプローチで、ASP.NETのWebアプリケーションを構成する際に問題点の分別、コードの単体テスト、TDDワークフローのサポートを簡単に行えるようになります。

このシリーズの最初の投稿では簡単な電子取引での製品の一覧表示や検索サイトを構築しました。そこではMVCの背後にある高いレベルの概念をカバーしており、ASP.NET MVCプロジェクトを何も無いところから作成し、この電子取引製品の一覧表示機能を実装・テストする方法をご紹介しました。 このシリーズの2度目の投稿では、ASP.NET MVCフレームワークのURLルーティングアーキテクチャーについて深く掘り下げ、それがどのように動作するのか、またより上級なURLルーティングシナリオでどのようにそれを処理するのかについてお話しいたしました。 3度目の投稿ではコントローラがビューとどのようにやり取りするのか、特にクライアントへ戻した応答を描画するために、コントローラからビューへデータを引き渡す方法についてお話しました。

本日の投稿では、MVCフレームワークを使用してフォーム入力やシナリオの投稿を処理するのに使用できるアプローチについて、またデータ編集シナリオを簡単に行えるようにするのに使用できるHTMLヘルパーのいくつかの拡張メソッドについてお話したいと思います。これらの概念をご説明するために以下で構築するアプリケーションの完成版をここをクリックしてダウンロードしてください。

フォーム入力と送信のシナリオ

ASP.NET MVCフレームワークでフォーム入力と送信のシナリオを処理する基礎部分をご説明するために、簡単な製品一覧、製品の作成、製品の編集シナリオを構築していきます。それは3つのコアなエンドユーザ体験が含まれます:

カテゴリ別製品一覧

/Products/Category/[CategoryID] URLに導くことでユーザは特定の製品カテゴリ内で全ての製品一覧を見ることができるようになります。:

新製品の追加

ユーザは"新製品の追加(Add New Product)"をクリックすると店舗に新製品を追加することができます。これにより/Products/New URL へリダイレクトされます。ここでは追加する新製品の詳細を入力するダイアログがポップアップします。:

保存をクリックすると、製品はデータベースに追加され、製品の一覧ページへリダイレクトされます。

製品の編集

製品一覧ページで、ユーザは各製品の隣にある"編集(Edit)"リンクをクリックすることができます。これにより/Products/Edit/[ProductID]URLへリダイレクトされます。ここではデータベースにそれらを更新するために保存(Save)ボタンをクリックして製品の詳細を変更することができます。:

データモデル

SQLサーバのNorthwindサンプルデータベースを使用してデータを保存します。そして、 .NET 3.5にビルドインされたLINQ to SQL オブジェクト・リレーショナル・マッパー(ORM)を使用して、データベースの行データを表すProduct、Category、Supplierオブジェクトをモデル化します。

まず、ASP.NET MVC プロジェクトの/Modelsサブフォルダを右クリックし、"新しい項目を追加"->"LINQ to SQL クラス"を選択して、 LINQ to SQL ORM デザイナを立ち上げ、データオブジェクトをモデル化します。:

そして、パーシャルのNorthwindDataContextクラスをプロジェクトに作成し、それに対していくつかヘルパーメソッドを追加します。これらのヘルパーメソッドを定義するのには2つの理由があります。: 1) 直接コントローラクラスにLINQクエリを埋め込むのを回避します。 2) 将来Dependency Injection(依存性注入)を使用するためにコントローラをより簡単に適用することができるようなります。

追加しようとしているNorthwindDataContextヘルパーメソッドは以下のようになります。:

LINQ や LINQ to SQLに関しては、LINQ to SQLシリーズをここからご覧ください。

ProductsControllerの構築

1つのコントローラクラスを使用して3つのコアなエンドユーザ検索体験を実装していきます。これを"ProductsController" と呼びます。(作成するには"Controllers"のサブフォルダを右クリックし、"新しい項目を追加" -> "MVC コントローラ"を選択します。):

このProductsControllerクラスは/Products/Category/3/Products/New/Products/Edit/5 などのURLを"Category"、"New"、"Edit" アクションを実装することで処理します。:

ProductsControllerクラスでこれらのURLがどのようにアクションメソッドにルートされるかについては、ASP.NET MVCシリーズのパート 1 パート 2をお読みください。このサンプルでは、デフォルトの /[Controller]/[Action]/[Id]ルートマッピングルールを使用します。つまり、ルーティングを発生させるためになにも構成する必要がありません

コントローラのアクションは出力を描画するために3つのビューページを使用します。"List.aspx"、"New.aspx"、"Edit.aspx"ページは\Views\Productsサブフォルダの下にあり、\Views\Sharedの下のSite.Masterマスターページを基にしています。

カテゴリ毎の製品一覧の実装

実装するサイトの最初の部分は製品一覧のURL (/Products/Category/[CategoryId])になります。:

この機能をProductsControllerクラスの"Category"アクションを使用して実装します。 LINQ to SQL の DataContextクラスと、それに追加したGetCategoryByIdヘルパーメソッド使用して、URL (例えば: /Products/Category/3)が示している特定のカテゴリCategoryオブジェクトを取得します。そして、"List"ビューにそこからのリスポンスを描画するためにCategoryオブジェクトを引き渡します。:

Listビューを実装する際、ViewPage<Category> から派生させるために最初にページのコードビハインドを更新します。それによりページのViewDataプロパティがコントローラから引き渡されたCategoryオブジェクトに型付けされます。(これについては、パート 3でもう少しお話しています。):

以下の様にList.aspxを実装します。:

上記のビューはCategory名をページの上部で描画し、カテゴリ内で黒丸付きの製品一覧を描画します。

黒丸付き一覧の各製品の横には"編集(Edit)"リンクがあります。押されたときに"Edit"アクションへユーザをリダイレクトするHTMLハイパーリンク(例えば: <a href="/Products/Edit/4">編集</a>)を描画するために、パート 2でお話したHtml.ActionLinkヘルパーメソッドを使用します。また、押されたときに"New"アクションへユーザをリダイレクトする<a href="/Products/New">新製品の追加</a>リンクを描画するために、ページの下部でHtml.ActionLinkヘルパーメソッドをその後再度使用します。

/Products/Category/1 URLを訪れ、ブラウザでソースを確認すれば、ASP.NET MVCアプリケーションがHTMLやURLのマークアップを非常にきれいに省いていることを確認できると思います。:

新製品の追加を実装 (パート 1 - 背景)

ではこれからサイトの"新製品の追加(Add New Product)"のフォーム送信機能を実装してみましょう。 /Products/New URLをユーザが訪れた時最終的に以下のようなスクリーンをご覧頂きたいと思います。 :

フォーム入力や編集シナリオは通常コントローラクラスで2つのアクションメソッドを公開することで、ASP.NET MVC フレームワークで処理されます。最初のコントローラアクションメソッドは初回表示用のフォームが含まれたHTMLを送信する役割を担っています。 2つ目のコントローラアクションメソッドはその後ブラウザから送られたフォーム送信の処理を行います。

例えば、上記の"製品の追加"スクリーンでは、2つの異なるProductsControllerアクション("New"および"Create")をまたいでそれを実装するように選択します。/Products/New URLは、製品の詳細を入力するためのテキストボックスとドロップダウンリストのHTMLコントロールが置かれた空のフォームを表示する役割を担います。このページにあるHTMLの<form> 要素は/Products/Create URLに設定された"action" 属性を持ちます。つまり、ユーザがフォーム送信ボタンを押した時に、そのフォームの入力が"Create" アクションへ送信されデータベースで処理・更新されます。

新製品の追加を実装 (パート 2 - 最初のアプローチ)

以下はProductsControllerに対して使用することができる初回の実装になります。

現在製品作製の工程に含まれるアクションメソッドが2つ("New"、"Create")あります。 "New"アクションメソッドは単純に空のフォームをユーザに表示します。 "Create"アクションメソッドはフォームから送信された値を処理し、新しい製品をデータベースに作成し、その後その製品に対してカテゴリの一覧表示ページへクライアントをリダイレクトします。

クライアントへ送信されたHTMLフォームは、"New"アクションメソッドで呼ばれた"New.aspx"ビュー内で実装されます。この初回実装(全てにテキストボックスを使用して)は以下の様になります。:

ページ上で標準のHTML<form>要素(runat=serverではなく)を使用している様子を上記でご確認ください。フォームの"action"属性はProductsControllerの"Create"アクションメソッドへ送信されるように設定されています。下部で <input type="submit"> 要素が押されたときに送信は行われます。これが行われると、ASP.NET MVCフレームワークは自動的にProductsControllerの"Create"アクションメソッドへメソッド引数として、ProductName、CategoryID、SupplierID、UnitPriceの値のマッピングを処理します。:

今サイトを起動すると、基本の製品入力機能が動作します。:

新製品の追加を実装 (パート 3 - ドロップダウンリストにHTMLヘルパーを使用)

前回のセクションで作成した製品の入力スクリーンは動作しますが、あまりユーザフレンドリーではありません。厳密にはエンドユーザが入力された製品の生のCategoryIDやSupplierID番号を知っていなければなりません。 HTMLのドロップダウンリストに可読性のある名前を表示するように修正を行う必要があります。

最初のステップはProductsControllerをビューの2つのコレクション(1つが利用可能なカテゴリの一覧で、もう1つが利用可能な供給者の一覧)へ引き渡す様に修正します。これらをカプセル化する強く型付けされたProductsNewViewDataを作成して対応します。この後それをビューへ引き渡します。(これについてはパート 3でご確認頂けます。):

そして、"New"アクションメソッドをこれらのコレクションを紐付け、"New"ビューにViewDataを引き渡すように修正します。:

ビューの中で、これらのコレクションを使用してHTMLの<select>ドロップダウンを生成することができます。

ASP.NET MVC HTML ヘルパー

ドロップダウンを生成するために使用できる1つのアプローチは手動でHTMLにif/elseステートメントが含まれた<% %> のfor文を作成します。これはHTMLを完全に制御できますが、HTMLがきたなくなってしまいます。

代わりに使用できるもっときれいなアプローチは、ViewPageベースクラスの"Html" ヘルパープロパティを活用することです。これは便利なオブジェクトでHTML UI生成を自動化するHTMLヘルパーUIメソッドを一式公開しています。例えば、この投稿の最初の方で、<a href="">要素を生成するためにHtml.ActionLinkヘルパーメソッドを使用しました。:

HtmlHelperオブジェクト(またはAjaxHelperオブジェクト、これについては後のチュートリアルでお話します。)は、"拡張メソッド(VS 2008リリースでのVBとC#の新しい言語機能です。)"を使用して簡単に拡張できるように特別に設計されています。つまり、誰でもこれらのオブジェクトに対して固有のヘルパーメソッドを作成し、共有で使用することができます。

ASP.NET MVCフレームワークの今後のプレビューでは数多くのHTMLおよびAJAXヘルパーメソッドがビルドインされます。最初のプレビューリリースでは "ActionLink"メソッドのみがSystem.Web.Extensions(ASP.NET MVCフレームワークが現在実装されているコアのアセンブリ)にビルドインされます。しかし、別に"MVCToolkit" のダウンロードもご提供しており、初回のプレビューリリースを使用していても、それをプロジェクトに追加していくつものヘルパーメソッドを獲得することができます。

MVCToolkit HTML ヘルパーメソッドをインストールするには、MVCToolkit.dllアセンブリをプロジェクトの参照に追加するだけです。:

プロジェクトをリビルドしてください。そうすると次回から<%= Html. %>とタイプすると多くの使用可能なUIヘルパーが表示されます。:

HTML <select> ドロップダウンを構築するために、Html.Select()メソッドを使用することができます。各メソッドはオーバーロードされたメソッドのバージョンと一緒に出てきて、全てビューの内部で完全なインテリセンスが使用できます。:

以下のコードを使用して、CategoryID/SupplierIDプロパティを値として、CategoryName/SupplierNameを表示名として使用するドロップダウンリストを表示するためにHtml.Selectオプションを使用するように"New"ビューを更新することができます。:

これは、実行時に適切な<select> HTMLマークアップを生成します。:

そうすれば、エンドユーザは製品カテゴリと供給者を/Products/Newのスクリーン上で選択する簡単な方法が得られるようになります:

注: CategoryIDやSupplierIDの値をサーバへ送信しているため、この新しいドロップダウンリストUIをサポートするために、ProductsControllerのCreateアクションを更新する必要は全くありません。普通に動作します。

新製品の追加を実装 (パート 4 - UpdateFromメソッドをCreateと一緒に使用したクリーニング)

ProductsControllerの"Create" アクションメソッドは"製品追加"シナリオのフォーム送信処理を担当しています。現在入ってきたフォームの引数をアクションメソッドへの引数として処理します。:

このアプローチはうまく動作しますが、多くの値が含まれたフォームに対しては、アクション上のメソッドシグネチャーがちょっと読みにくくなってしまいます。新しいProductオブジェクトに対して入ってきた引数を全て設定する上記のコードも少し長くて単調です。

もしMVCToolkitアセンブリを参照している場合、少しこれをきれいにすることのできるSystem.Web.Mvc.BindingHelpers名前空間内に実装されている有用な拡張メソッドをオプションとして活用することができます。これは"UpdateFrom"と呼ばれ、どの.NETオブジェクト上ででも使用することができます。これは引数として値のディクショナリを取り、その後そのオブジェクト上の公開プロパティに合った全てのキーに対して自分自身に自動的にプロパティの割り当てを実行します。

例えば、UpdateFromメソッドを使用するために上記のCreateアクションメソッドを書き直すことができます。:

 

注: もしもっとセキュリティ上厳しくして特定のプロパティのみの更新しか許可しない場合は、オプションとして更新するプロパティ名の文字列の配列をUpdateFromメソッドに引き渡すこともできます。:

製品の編集機能の実装 (パート 1 - 背景)

ではこれからサイトの"製品の編集(Edit Product)"機能を実装してみましょう。 /Products/Edit/[ProductID] URLをユーザが訪れた時最終的に以下のようなスクリーンをご覧頂きたいと思います。:

上記の"新製品の追加"のフォーム送信例の様に、このフォームの編集対応を2つのProductsControllerアクション("Edit" と"Update")を使用して実装します。:

"Edit" は製品フォームを表示します。"Update" はフォーム送信アクションを処理するために使用します。

製品の編集機能の実装 (パート 2 - Editアクション)

まず、アプリケーションの編集機能をProductControllerのEditアクションメソッドを実装することで有効化します。この投稿の最初で製品の一覧ページを作成した時、EditアクションがURLの一部としてID引数を取れるように構築しました。(例えば: /Products/Edit/5):

データベースから適切な製品オブジェクトの取得、そして利用可能な供給者やカテゴリコレクションの取得(そうすれば編集ビューでドロップダウンを実行できるため)を行うEditアクションメソッドが必要です。以下の様にProductsEditViewDataオブジェクトを使用してこれを全て表示するための強く型付けされたビューオブジェクトを定義します。 :

そしてこのViewDataオブジェクトを紐付けして、それを"Edit"ビューで描画するためにEditアクションメソッドを実装します。 :

製品の編集機能の実装 (パート 2 - Editビュー)

以下のアプローチを使用して"Edit.aspx"ビューページを実装することができます。:

Html.TextBoxとHtml.Selectヘルパーメソッドの両方を上記のサンプルでどのように使用しているかをご確認ください。これら両方はMVCToolkit.dllアセンブリからの拡張メソッドです。

Html.Selectヘルパーメソッドにはオーバーロードされたバージョンがあり、それによりドロップダウンリストにある選択された値を特定することができるようになっている様子もご確認ください。以下のスニペットでは、製品の現在のCategoryIDの値に基づいて自動的に選択されたカテゴリのドロップダウンの項目が必要であることを示しています。:

最後に、<form>要素の"action"属性を設定するためにUrl.Action()ヘルパーメソッドを使用している様子をご確認ください。:

Url.ActionおよびHtml.ActionLinkヘルパーメソッドの両方はASP.NET MVC フレームワークのルーティングエンジン を使用してURLを生成します。(URL生成の動作に関する詳細はパート 2 をお読みください。)つまり、サイトでEditに対するルーティングルールを変更した場合、コントローラやビューでどのコードも変更する必要は ありません 。例えば、RESTがたくさんあるURL、/Products/Edit/1の代わりに/Products/1/Editがある様なURLを使用するためにURLをマップし直すことができ、上記のコントローラやビューを修正なしで動作させることができます。

製品の編集機能の実装 (パート 3 - Updateアクション)

最後のステップは"Update" アクションメソッドをProductControllerクラスで実装することです。

前回の"Create"アクションメソッドのように、"UpdateFrom" 拡張メソッドを活用して自動的にリクエストからの製品オブジェクトを紐付けます。空の製品オブジェクトを紐付けるのではなく、最初にデータベースから古い値を取得し、ユーザがそれに変更を適用し、それをデータベースに保存したパターンを使用します。

編集が行われると、製品一覧のページにリダイレクトされ、作業に使用していた製品の保存状態に合致した/Products/Category/[CategoryID]が自動的に設定されます。

まとめ

この投稿がフォーム入力および送信シナリオをASP.NET MVC フレームワークで処理する方法についての詳細のご提供と、よくあるデータ入力や編集シナリオの処理と構造化に対してコンテキストをご提供できていれば幸いです。

上記で構築したアプリケーションの完成版のソースコードが含まれているZipファイルをダウンロードされる方は、 ここをクリックしてください。

今後の投稿で、フォーム入力および編集のシナリオで検証とエラーリカバリのシチュエーションを処理する方法をカバーしたいと思います。早急なアプリケーション作成を可能にするデータとセキュリティの足場サポートをすこしお話します。MVC フレームワークを使用したAJAXを有効化した編集を実行するためのASP.NET AJAXの使用方法についてお話します。また、単体テストの方法とコントローラへのDependency Injection(依存性注入)の追加の方法についてもっと深く掘り下げたいと思います。

Hope this helps,

Scott

ScottGu's blog translated by Chica @ Wankuma 

※本翻訳に関しまして、Scottさんにはご了承頂いております。