ASP.NET MVCをあまりいじることなく、ViewData/ViewModelアプローチを実装する方法を見つけました。
これが私がそれをした方法です:
これはViewPage2
、強く型付けされたViewDataオブジェクトをビューに公開する私のクラスです。残念ながら、新しいViewDataDictionaryを作成するViewPageの動作を回避するには、リフレクションを使用する必要がありますが、静的にキャッシュされたFieldInfoオブジェクトを使用すると、ルーティングでのMVCの動的コントローラー/アクションルックアップよりもコストがかかりません。
public class ViewPage2<TModel,TData> : ViewPage<TModel> where TData : ViewDataDictionary<TModel> {
public ViewPage2() : base() {
}
private Boolean _dataPresent;
private TData _data;
public new TData ViewData {
get {
if( _dataPresent && _data == null ) {
_data = (TData)base.ViewData;
}
return _data;
}
}
// Cached in static class state for performance.
private static readonly FieldInfo _viewPage1ViewData;
private static readonly FieldInfo _viewPage2ViewData;
static ViewPage2() {
Type viewPage1 = typeof(ViewPage<TModel>);
_viewPage1ViewData = viewPage1.GetField("_viewData", BindingFlags.Instance | BindingFlags.NonPublic );
Type viewPage2 = typeof(ViewPage);
_viewPage2ViewData = viewPage2.GetField("_viewData", BindingFlags.Instance | BindingFlags.NonPublic );
}
protected override void SetViewData(ViewDataDictionary viewData) {
// ViewPage<TModel> creates a new ViewDataDictionary<TModel> when this method is called, even if viewData is of the correct type.
// The trick is to reimplement SetViewData and set base._viewData and basebase._viewData
if( viewData is TData ) {
_viewPage1ViewData.SetValue( this, viewData );
_viewPage2ViewData.SetValue( this, viewData );
_dataPresent = true;
} else {
base.SetViewData( viewData );
}
}
}
次に、*。aspxファイルごとに(私はWebFormViewEngineを使用します)、@Page
ディレクティブを変更するだけです。
<%@ Page Language="C#" MasterPageFile="~/Site.Master" Inherits="Me.ViewPage2<Me.FormModel,Me.FormData>" %>
少し面倒にするために、2つの汎用型指定子を認めますが、これは1回だけ設定する必要があるものです。
次に、各コントローラーでこれを行うだけです。
public ActionResult Edit() {
FormData data = new FormData();
data.SomeStronglyTypedField = "foo";
this.ViewData = data;
}
各ビューで、強く型付けされたビューデータを利用できるようになりました。
<p><%= ViewData.SomeStronglyTypedField %></p>
また、ViewDataはモデルの一部ではないため、POSTで送信されたデータを処理する際の関心の分離と、自動バインドの利点があります。
[HttpPost]
public ActionResult Edit(EditModel model) {
if( !ModelState.IsValid ) {
// See how I can return the model object without modifying it. All I need to do is re-create the View data.
FormData data = new FormData();
data.SomeStronglyTypedField = "foo";
this.ViewData = data;
return View( model );
}
// persist to DB here
return RedirectToAction("View");
}
実際には、ViewDataオブジェクトの自動作成と設定を処理する共通のBaseControllerクラスを使用しているためFormData data...
、すべてのアクションでこれらの3行(など)は必要ありません。