2012-07-07 67 views
1

在我正在開發的一個項目中,我有很多「只讀」頁面,沒有<form> s;我也有很多表單頁面,這些頁面也有很多從控制器中讀取的只讀數據。ASP.NET MVC - 類型ViewData提案

通常情況下,您需要使用ViewModels,並且每個視圖有一個ViewModel,並且ViewModel包含該視圖的所有數據。這似乎很公平,除了有一個問題:

在我的腦海中,我將ViewModel看作是從視圖發送回控制器的整個數據的表示形式和封裝,但ViewModel可能包含由控制器(如SelectListItem [] Html.DropDownListFor()數據)不能由視圖填充併發送回控制器。

當然,可以將這些數據作爲ViewModel的一部分,並在控制器的HttpPost處理方法中返回視圖之前手動重新填充它,但是我覺得它不必要地使控制器的代碼複雜化(而且你會必須使用UpdateModel()而不是在將模型指定爲操作方法的參數時發生的自動更新)。

我對此的解決方案是一個類型化的ViewData對象。我從ViewPage<TModel>派生給ViewPage2<TModel,TData> where TData : ViewDataDictionary<TModel>和覆蓋(或陰影)的.ViewData屬性返回TDATA,而不是一個實例..

我的問題是雙重的:

  • 子類的ViewPage似乎很容易,但我在哪裏把邏輯來處理我的ViewPage2<TModel,TData>類的初始化?
  • 我的方法有什麼問題嗎?
+0

一種方法是:單個視圖模型可以具有稱爲ViewObjects(屬性集合)的屬性,以在主視圖中填充只讀部分。 – 2012-07-07 04:03:49

回答

0

我找到了一種方法來實現我的ViewData/ViewModel方法,而不用太多的ASP.NET MVC。

我是這樣做的:

這是我ViewPage2類暴露的強類型的ViewData對象的意見。不幸的是,你需要使用反射來避免ViewPage創建一個全新的ViewDataDictionary的行爲,但是靜態緩存的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>" %> 

我承認這兩個泛型類型說明符讓它有點繁瑣,但這是你只需要設置一次的東西。

然後在每個控制器,它只是一個這樣的事情:

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"); 
} 

實際上,我使用一個通用的BaseController類來處理ViewData對象的自動創建和設置,因此我不需要在每個操作中使用這三行(FormData data...等)。