はじめてのRazorらぞぅ(4) DBマイグレーション編
- はじめてのRazorらぞぅ http://d.hatena.ne.jp/torazuka/20130723/dotnetmvc
- はじめてのRazorらぞぅ(2) http://d.hatena.ne.jp/torazuka/20130724/dotnetmvc
- はじめてのRazorらぞぅ(3) http://d.hatena.ne.jp/torazuka/20130725/dotnetmvc
今日はRazorタイムが取れたので、上の続きです。
やったこと
学んだこと
アクションメソッド呼び出し
前回メモしたとおり、Html.ActionLinkでハイパーリンクを作成できる。このとき、別のコントローラのアクションメソッドを呼び出したいときは、どうすればよいか? 答、BeginRouteFormを使う。
- ASP.NET MVC 3 開発入門 (16) - HTML ヘルパーを活用 http://shiba-yan.hatenablog.jp/entry/20110327/1301152413
素晴らしい記事のおかげで解決しました。ありがとうございます。
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のアップデート時にその行を作ってくれる。つまり、テストデータのセッティングに使えるらしい。
手順はこんなかんじ。
- Database Explorerで、 Data Connectionsのツリーの中にあるDBContextを消す
- Solution Explorerで、App_Dataの下にある.mdfを消す
- Package Manager Consoleで、コマンド「Enable-Migrations -ContextTypeName {DBContext名}」を実行
- 生成されたMigrations\Configuration.csのSeedメソッドを編集して、初期データを定義
- using {モデル名}で参照を解決
- ソリューションをビルド
- Package Manager Consoleで、コマンド「add-migration Initial」を実行
- Migrations\{DateStamp}_Initial.csが生成される
- コマンド引数が、生成されるファイルのサフィックスになる
- 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の中に書くのはよくないヨネ、きっと。
あと、テストのやり方どうしようかな。