ASP.NET MVCについてもチマチマと、とりあえず基本のあたり(4)

IControllerFactory、IViewEngine、フィルタときて、最後はIModelBinderについて(・∀・)

IModelBinder

モデルバインダとは何かと言えば、画面からのリクエスト時に、モデルのインスタンスを作成して、POSTされたパラメータ等からモデルに値を設定してくれる仕組み。
例えば、以下のようなビューモデルと画面を作成した時に、コントローラでそのインスタンスを受け取ることが出来るのはモデルバインダのおかげ(´-`)

public class HogeViewData
{
    public int Id { get; set; }
    public string Name { get; set; }
}
<% using (Html.BeginForm()) { %>
  <div>
    <fieldset>
      <legend>データ</legend>
      <p>
        <label for="id">Id:</label>
        <%= Html.TextBox("id") %>
        <%= Html.ValidationMessage( "id", "*" ) %>
      </p>
      <p>
        <label for="name">name:</label>
        <%= Html.TextBox( "name" ) %>
        <%= Html.ValidationMessage( "name", "*" ) %>
      </p>
      <p>
        <input type="submit" value="登録" />
      </p>
    </fieldset>
  </div>
<% } %>
public class HogeController : Controller
{
...
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Index(HogeViewData data)
    {
        // ここでdataに画面で入力した値が設定されているのは
        // モデルバインダの仕組みのおかげヽ(´ー`)ノ
        // ついでに入力エラーがModelStateに設定されているのも

        return View();
    }
}

っで、標準で使用されるIModelBinderの実装はDefaultModelBinderクラスになります。
DefaultModelBinderはHttpRequest.Formの値からモデルを構築し、変換できない項目やモデルクラスがIDataErrorInfoを実装している場合には、エラー項目をModelStateに追加してくれるような作りになっています。


っということで、使用するIModelBinderの指定方法と、DefaultModelBinderの拡張方法について見ていきます(・∀・)

IModelBinderの指定方法

例として、下記の様なHogeViewDataビューモデル用のIModelBinder実装を作成し、DefaultModelBinderの代わりにこのHogeViewDataBinderを使用する方法を見てみます。

public class HogeViewDataBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        HogeViewData data = new HogeViewData();

        // controllerContext.HttpContext.Request.Formとかから値設定
...
        return data;
    }
}

まず、モデルクラスにModelBinderAttributeを指定して、そのモデルに対して使用するIModelBinderを指定する方法があります。

[ModelBinder(typeof(HogeViewDataBinder))]
public class HogeViewData
{
...
}

モデルに対してCustomModelBinderAttribute派生クラスが付加されている場合、フレームワークはそこから使用するIModelBinderの実装を取得します。
ModelBinderAttributeもCustomModelBinderAttributeの派生クラスであり、IModelBinder実装のTypeを指定することにより、その実装が使用されるようになります。


また、モデルに対して属性を付加するのではなく、以下のように事前にモデルの型と使用するIModelBinder実装を指定しておくことでも、その実装が使用されるようになります。

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
...
        // 型に対して使用するIModelBinderを指定
        ModelBinders.Binders.Add( typeof(HogeViewData), new HogeViewDataBinder() );
    }
}

その他の指定方法としては、コントローラのアクションメソッドで、パラメータに対してModelBinderAttributeを指定する方法もあります。

public class HogeController : Controller
{
    public ActionResult Index([ModelBinder(typeof(HogeViewDataBinder))] HogeViewData data)
    {
        // HogeViewDataBinderを使用して作成されたHogeViewDataモデルを取得
    }
}

この場合、ModelBinderAttributeを付加したメソッドでのみ指定したIModelBinder実装が使用され、そうで無いメソッドでは規定のIModelBinder実装が使用されます。
これにより、同じモデルに対しても、アクションメソッド毎に使用するIModelBinder実装を切り替えることが可能です(・∀・)


また、特定のモデルに結びつけたIModelBinder実装を指定するのではなくて、標準で使用されるIModelBinderを変更する場合には、下記の様にデフォルトの実装を指定します。

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
...
        ModelBinders.Binders.DefaultBinder = new CustomModelBinder();
    }
}

例えば、DefaultModelBinderを拡張して独自の機能を追加したIModelBinderを標準で使用するときにはこの指定を行います。

DefaultModelBinderの拡張

っということで、DefaultModelBinderの拡張についても見てみます(・∀・)
DefaultModelBinderにはいくつかの拡張ポイントが用意されていて、主なところでは以下のようなものがあります。

public class DefaultModelBinder : IModelBinder
{
    protected virtual bool OnModelUpdating();
    protected virtual void OnModelUpdated();
    protected virtual bool OnPropertyValidating();
    protected virtual void OnPropertyValidated();
}

OnModelXxx()はモデル構築前後の拡張ポイント、OnPropertyXxx()はモデルの各プロパティ設定前後の拡張ポイントです。
DefaultModelBinderの実装では、OnModelUpdated()、OnPropertyValidated()ではターゲットのモデルがIDataErrorInfoを実装している場合に、エラー項目をModelStateに追加する処理が入っています。
また、モデルのメンバがint型の所に空を入力したような場合に、ModelStateに必須エラーが追加されるのは、OnPropertyValidating()にチェック処理が入っているからですね(・∀・)*1


っで、これらの拡張ポイントをoverrideすることにより、独自のヴァリデーション機構を持ったDefaultModelBinder拡張なんかの作成が可能になります(・∀・)
例えば、Data Annotations Model Binder SampleにDataAnnotationsを使った使ったヴァリデーションを行うDefaultModelBinder派生のサンプルがあったりします。



…っというわけで、主にフレームワークの拡張ポイントを中心にASP.NET MVCの基本を見てきました。
まあ、だいたいASP.NET MVCの概要も分かったので、これから趣味的なものはASP.NET MVCで作っていこうかと思います(`・ω・´)


もっかの悩みは、アーキテクチャとしてモデル層をどう構築するかというあたり(´ω`)

*1:型変換ができないときのエラーメッセージはBindProperty()のあたりの処理。