連載:〜ScottGu氏のブログより〜

ASP.NET MVC Preview 5とフォーム送信シナリオ

Scott Guthrie 著/Chica
2008/9/12
Page1 Page2 Page3 Page4

■LINQ to SQL Entityへのビジネス・ルールの追加

 上記のサンプルでは、Productエンティティを定義し、LINQ to SQLを使用してデータ・アクセスを行っています。現状では、Productエンティティ上で使用しているドメインのルールや検証のレベルは、SQL Serverのメタデータ(Null、データの型、長さなど)からLINQ to SQLにより推測されたものです。これは上記のようなシナリオ(Decimal型へほかの型の入力を割り当てるような場合)を補足します。しかしながら、SQLのメタデータを使用するだけでは簡単には宣言できないようなビジネスの問題点はモデル化できません。例えば、販売中止の製品の再注文レベルを0以上に設定できないようにしたり、仕入れ価格より低い価格で製品を販売することができないようにしたりするなどです。こういったシナリオに対しては、それらのビジネス・ルールを表現および統合するために、モデルにコードを追加する必要があります。

 このビジネス・ルールのロジックを適用するのにふさわしくない場所は、アプリケーションのUI層です。これには多くの理由があります。その中でも大きいのは、重複したコードが発生する可能性が高いことです。これは、結果的にUIからUIへ、フォームからフォームへとルールをコピーすることになるだろうと思われるからです。時間がかかるということ以外にも、ビジネス・ルールのロジックを変更した際に、バグが発生したり更新する場所を忘れたりする大きな理由になりかねません。

 これらのビジネス・ルールを組み込む一番よい場所は、モデルまたはドメイン・レベルです。そうすると、どのような形のUI、フォーム、サービスでも、それらを使用および適用できます。ルールの変更は一度で済み、ロジックを重複することなく、どこからでも利用することができます。

 上記で使用しているProductモデル・オブジェクトに対して、よりリッチなビジネス・ルールを統合するために利用可能なパターンや方法はいくつかあります。ルールは、オブジェクト内外で定義できます。宣言的なルール、再利用可能なルール・エンジンのフレームワーク、命令コードなどを使用できます。キーポイントは、ASP.NET MVCでこれらすべての方法が利用できることです(1つの方法しか取れない機能は多くないので、どのような形でもそれらを反映できる柔軟性があります。MVCの特徴は、たいていどのようなものにでも統合できる拡張性です)。

 このブログでは、比較的簡単なルールのアプローチを取っていきます。まず、“RuleViolation”クラスを以下のように定義して、モデル内で違反のあったビジネス・ルールについての情報を捕捉します。このクラスはエラーの詳細を表示するErrorMessage文字列、そしてその違反に関連した主要なプロパティ名や値を公開します(図32)。


図32

(注意:簡単にするために、プロパティは1つだけを使用します。もっと複雑なアプリケーションでは、これをリストにすれば複数のプロパティが特定できるようになります)

 そして、GetRuleViolations()という1つのメソッドを持つIRuleEntityを定義します。このメソッドは、そのエンティティにあるすべての現在違反しているビジネス・ルールのリストを返します(図33)。


図33

 その後、このインターフェイスを実装するProductクラスを作成します。サンプルを簡単にするために、メソッドの中にルールの定義と評価ロジックを埋め込みます。再利用可能なルールを利用したり、より複雑なルールを処理したりするような、もっとよいパターンもあります。もしこのサンプルが大きくなるようであれば、このメソッドをリファクタリングしてルールやその評価定義を別の場所で定義しますが、今回は簡単なものにしておきたいので、以下のように3つのビジネス・ルールを評価するだけにします(図34)。


図34

 アプリケーションはProduct(またはそのほかのIRuleEntity)インスタンスを検索して、現在の検証状態をチェックしたり、エンドユーザーが修正できるようにガイドするためのUI表示で利用できるRuleViolationオブジェクトの取得を行ったりできます。また、これにより、アプリケーションのUIとは独立してビジネス・ルールの単体テストが簡単に行えるようになります。


図35

 特にこのサンプルでは、データベースにProductオブジェクトが無効な状態で絶対保存されないように強化してみます(つまり、すべてのRuleViolationは、Productオブジェクトがデータベースに保存される前に修正されていなければなりません)。これは、OnValidate部分メソッドをProductの部分クラスへ追加して、LINQ to SQL を使用することで行えます。このメソッドは、データベースの保存を行うタイミングでLINQ to SQLから自動的に呼び出されます。以下では、上記で追加したGetRuleViolations()メソッドを呼び出して、未解決なエラーがあった場合に例外を発生させています。これによりトランザクションは中止され、データベースは更新されません(図36)。


図36

 ProductからRuleViolationを取得できるようにするユーザーフレンドリーなヘルパー・メソッドを持つと同時に、これらのRuleViolationが、データベースが更新される前に修正されるように強化できました。

■上記のルールをASP.NET MVC UIへ統合

 ビジネス・ルールを実装して、上記のようにRuleViolationを公開しておけば、それをASP.NET MVCサンプルに比較的簡単に統合できます。

 ProductクラスにOnValidate部分メソッドを追加したので、northwind.SubmitChanges()を呼び出したときに、保存しようとしているProductオブジェクトにビジネス・ルールの検証で問題があれば、例外が発生します。この例外は、すべてのデータベースのトランザクションを中止して、以下のようにCatchブロックで捕捉されます(図37)。


図37

 エラーのCatchブロックに追加するもう1行のコードは、以下のように定義したUpdateModelStateWithViolations()ヘルパー・メソッドを呼び出すロジックです。このメソッドは、エンティティからすべてのルール違反を取得し、ModelStateコレクションを適切なモデル・エラー(それらを発生させるエンティティ・モデル上のプロパティへの参照を含む)で更新します(図38)。


図38

 これを行って、アプリケーションを再実行します。そうすると、エラー・メッセージに関連した入力形式に加えて、ASP.NET MVC の検証ヘルパーがビジネス・ルールの違反も表示するようになります。

 例えば、単価に1ドル以下を設定し、再注文レベルを-1に設定するとします(両方の値は、入力形式の観点からは正しいものですが、ビジネス・ルールに違反しています)。保存を押すと、Html.ValidationSummary()リストでエラーが表示され、対応したテキストボックスに印が表示されます(図39)。


図39

 ビジネス・ルールは複数のProductのプロパティをカバーできます。例えば、もし製品が販売中止の場合、再注文レベルを0以上にできないというルールが追加されているのを確認できます(図40)。


図40

 このビジネス・ルールのプロセス全体を通じて、“Edit”ビューのテンプレートへ加える変更は、2つのProductプロパティ(ReorderおよびDiscontinued)をファイルに追加するだけです(図41)。


図41

 Productエンティティへは、ビジネスの検証ルールを好きなだけ追加することができ、UIがそれらをサポートするためにEditビューやProductsControllerを更新する必要はありません。

 コントローラやビューとは別に、モデルやビジネス・ルールを単体テストすることができます。コントローラ、ビュー、モデルとは別に、URLのルーティングも単体テストすることができます。また、ビューとは別に、コントローラの単体テストをすることもできます。このブログにあるすべてのシナリオで、モックやスタブを使用しない単体テストがサポートされています。結果、アプリケーションが簡単にテストでき、これにより優れたTDDワークフローのサポートが可能になります。

まとめ

 今回のブログでは、ASP.NET MVC Preview 5で動作するフォーム送信のシナリオを簡単に見てきました。読み終わった後、MVCモデルを使用したフォームと入力シナリオの処理方法についてお分かりいただいていれば幸いです。上記で構築したアプリケーションの完成C#バージョンは、ここからダウンロードできます。VBバージョンは今週末くらいにアップする予定です(いま午前4:30で、残念ながら、あと数時間で飛行機に乗らなきゃいけないのに、まだパッキングも終わってないんです)。

重要:もしMVCモデルが嫌いだったり、ご自分のスタイルの開発には不自然だと感じられた場合、まったく使用する必要はありません。これはまったくのオプションですし、既存のWebフォーム・モデルを置き換えるものではありません。WebフォームもMVCもどちらも今後サポートされ、改善されていきます(ASP.NET Webフォームの次のリリースでは、よりリッチなURLルーティング機能、よりよいHTMLタグ、クライアント・サイドID、CSSサポートなどが追加されます)。上記をお読みになった後で、“いや、これは私にはしっくりこない”と思われたら、お気になさらず、これを使用しなければいけないなどと思わないでください(そうする必要はありません)。

 次のMVCの投稿では、ASP.NET MVCアプリケーションでAJAXの統合方法をカバーしたいと思っています。

 Hope this helps,

 ScottEnd of Article

 

 INDEX
  〜ScottGu氏のブログより〜
  ASP.NET MVC Preview 5とフォーム送信シナリオ
    1.Web MVCパターンによる基本的なフォーム送信(Form Post)
    2.モデル・バインダ(Model Binder)/UpdateModelおよびTryUpdateModelメソッド
    3.エラー処理のシナリオ ―― フォームの再表示でエラー・メッセージを表示
  4.LINQ to SQL Entityへのビジネス・ルールの追加
 
インデックス・ページヘ  「〜ScottGu氏のブログより〜」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間