はじめてのRazorらぞぅ(4) DBマイグレーション編

今日はRazorタイムが取れたので、上の続きです。

目標
CRUDのスケルトンを動かしてテストの書き方を把握するところまで。

やったこと

次のチュートリアルを最後まで。モデルに新しいプロパティを追加して、DBをマイグレーションする等。

学んだこと

アクションメソッド呼び出し

前回メモしたとおり、Html.ActionLinkでハイパーリンクを作成できる。このとき、別のコントローラのアクションメソッドを呼び出したいときは、どうすればよいか? 答、BeginRouteFormを使う。

素晴らしい記事のおかげで解決しました。ありがとうございます。

POSTリクエストの処理

POSTリクエストを処理するアクションメソッドには、HttpPostアトリビュートをつける。GETはデフォルトなので、特に指定しなくてよい。

チュートリアルでは、アクションメソッドの実行中にシステムエラー(たとえば、編集しようとしたオブジェクトが存在しないなど)が発生した場合、HttpNotFoundResultのインスタンスをViewに返している。ロジックエラーの場合は、別のアクションメソッドを呼ぶこともあるのかも。

また、チュートリアルでは、POSTの処理が成功したら元の画面にリダイレクトしている。このとき、処理を実行したことを画面に通知する(例: 「○○を更新しました」)には、どうするのがよいか。ViewBagにフラグを持たせて、View Template側でチェックしてメッセージを出す? JavaScriptでやる? サテハテ…

「id」という名前のパラメータを使ったときの挙動

アクションメソッドの引数はURLのパラメータになる。「http://{サーバ}/{controller}/{action}?パラメータ名=値」でアクションを実行できる。

しかし、パラメータ名が「id」のときは、「http://{サーバ}/{controller}/{action}/{id}」にマッチする。

DBマイグレーション

ASP.NET MVCでは、Modelクラスのコードに基づいて、元にDBを作成したり更新したりする。Code Firstという。

Code Firstマイグレーションが実行されるごとに、Configuration.csのSeedメソッドが呼び出される。なので、Seedメソッド内で必要なモデルのインスタンスを定義しておくと、DBのアップデート時にその行を作ってくれる。つまり、テストデータのセッティングに使えるらしい。

手順はこんなかんじ。

  1. Database Explorerで、 Data Connectionsのツリーの中にあるDBContextを消す
  2. Solution Explorerで、App_Dataの下にある.mdfを消す
  3. Package Manager Consoleで、コマンド「Enable-Migrations -ContextTypeName {DBContext名}」を実行
  4. 生成されたMigrations\Configuration.csのSeedメソッドを編集して、初期データを定義
    • using {モデル名}で参照を解決
  5. ソリューションをビルド
  6. Package Manager Consoleで、コマンド「add-migration Initial」を実行
    • Migrations\{DateStamp}_Initial.csが生成される
    • コマンド引数が、生成されるファイルのサフィックスになる
  7. Package Manager Consoleで、コマンド「update-database」を実行

以上で、DBが再作成されて、Seedに定義したデータが投入される。

バリデーション

ViewにもControllerにも手を加えることなく、Modelクラスのプロパティにアノテーションを付けるだけで、バリデーションが追加されるのが肝。しかも、サーバサイドのバリデーションと同時に、クライアントサイドのバリデーションも一緒に追加してくれる。

気になったこと。モデルクラスにアノテーションを付けた後、DBマイグレーションをしていないのに、(チュートリアルにあるような)実行時エラーが出ませんでした。

        [Range(1, 100)]
        [DataType(DataType.Currency)]
        public decimal Price { get; set; }

上記のアノテーションが、自動生成したマイグレーション用のファイルに反映されません。しかし、アプリを実行すると、バリデーションが正しく実行されます。

あとから分かったのですが、DataType属性はViewエンジンに与えるレンダリングのヒントに過ぎず、入力値のバリデーションではないそうです(だからマイグレーションファイルに記述が出力されなかったのでしょうか)。フォーマットのバリデーションをしたい場合は、 RegularExpression属性を使うそうです。でも、Rangeはバリデーションでは? なんでマイグレーションファイルに出てこないの? ぬーん

エラーメッセージの言語

画面に表示されるバリデーションエラーのメッセージは、inputタグのdata-val-required属性にセットされる。たとえば、「Title フィールドが必要です」など。このメッセージの編集方法、あるいは、languageの指定方法を知りたい。というのも、アノテーションで与えた検証に引っ掛かると、エラーメッセージは日本語だが、アノテーション以外のデフォルトの検証(があるぽい)に引っ掛かると、エラーメッセージが英語になるから。言語を統一したい。

このへんかなー。

GETとPOSTのメソッドが同じシグネチャの時の解決法

GETとPOSTで同じ名前のアクションメソッドを使いたいとき、シグネチャが同じだとオーバーロードができなくて困る。

解決法は2つ提示されている。

1つ目は、POST用のアクションメソッドを違う名前で定義する方法。

        public ActionResult Delete(int id = 0)
(略)

        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)

「ActionName("Delete")」のおかげで、DeleteメソッドがPOSTで呼び出されたときには、DeleteConfirmedが呼び出される。

2つ目は、使用しない引数を渡して、無理やりシグネチャを変える方法。

チュートリアルでは、1つ目の解決法が採用されている。

迷っているところ

入力値に対する「DBアクセスを伴うバリデーション」をどこに書けばよいのか。カスタム検証用のクラスを作ればいいのか。Controllerの中に書くのはよくないヨネ、きっと。

あと、テストのやり方どうしようかな。