そもそもModelStateってなんなんだ

概要

ASP.NET MVCで使用するModelStateってなんのためにあるのでしょう?
ModelState.Clearってあるけど、なんのために実行しているのでしょう?
ViewModelとModelStateの関係性ってなんなんでしょう?
ModelStateのライフサイクルはいつからいつまでなのでしょう?
そこらへんの説明になります。

ModelStateの役割

ModelStateの役割は、大きく2つあり、画面に入力した内容の保持と、画面入力値に対する入力チェック(Validation)時のエラーメッセージの保持になります。

例えば、画面からのリクエストをコントローラーで受け取った際に、内容に不備があれば、前回入力した値を使って再度入力画面を表示する、という処理を考えてみます。

画面へのデータの表示のために、通常はViewModelを用意し、ViewModelに画面に表示したい内容をセットして、View出力します。
Viewにユーザがデータを入力し、入力データがサーバにリクエストされます。
コントローラーはリクエストから入力データを受け取り、入力チェックを行います。
入力チェックの結果、不備があった場合、再度入力画面のViewを出力します。
その際に、ViewModelを入力された値で作り直さなくても、入力データがModelStateに保持されているため、入力画面には(ModelStateで保持されている)前回の入力値が表示されます。
これは、ModelStateがViewModelよりも優先されて参照されるからです。

ModelState.Clearしないとどうなるか

画面を表示する際にViewModelよりもModelStateが優先されるため、コントローラでViewModelを書き換えても、ModelState.Clearしなければ画面で入力された内容のまま表示されてしまいます。

ModelState.Clearをした場合

ModelState.Clearを行うと、ViewModelの値のみで画面を表示するため、ViewModelに存在しない値(今回の例だと生年月日)は表示されません。

ModelStateのライフサイクル

ModelStateはリクエスト時にModelBinderによってリクエストの入力値がセットされ、レスポンス時にViewから参照されます。なのでライフサイクルはリクエスト単位になります。

このライフサイクルで困るのは、以下のようなケースになります。

コントローラに画面表示用のGETメソッド「Display」と、データ登録用のPOSTメソッド「Regist」があり、Registで登録する際に入力データに不備があったため、再度画面を表示しようとするケース。

Regist内でViewModelを生成し、ModelStateをクリアせずにReturn Viewすれば問題ないですが、そうすると画面表示の機能がDisplayとRegistで重複してしまいます。
またRegistはPOSTのため、Regist内で画面表示すると、ブラウザ側でF5キー押下などで画面再描画すると、Registが動作し、再度登録処理が行われてしまいます(結果、入力エラーにはなりますが)。
Registで入力エラーだった場合は、Displayにリダイレクトして画面表示したいと思うところです。

ところが、ModelStateのライフサイクルはリクエスト単位のため、リダイレクト先にまではModelStateが引き継がれません。画面で入力された値を引き継ぎたい場合、入力値をDisplayのクエリーパラメータで受け取って表示する必要があります(ModelStateの恩恵を受けられない)。
同様に入力チェック時のエラーメッセージもDisplayのクエリーパラメータで受け取る必要があります。

実際のところ、そんなことをやっているかというと、やってなくて、RegistメソッドでTempDataというセッションにModelStateを保存し、DisplayでTempDataからModelStateを復帰するということでこの問題を回避しています。
やり方は、PRGパターンでのModelStateの持ち回りをご参照ください。