2009-11-30 33 views
4

我的應用程序中有一對視圖,它們都顯示了我的模型項目的相同編輯器模板; (「添加」和「編輯」),「編輯」工作正常,但是當我的控制器動作處理帖子時,「添加」對模型返回空值。ASP.NET MVC - 表單返回空模型,除非模型被包裝在自定義視圖模型中

我發現如果我給「自定義ViewModel」添加視圖並調用Html.EditorFor(p => p.PageContent)而不是簡單地調用EditorFor()在整個Model對象-上,那麼表單將返回正確的非null模型,但這會產生與我的客戶端腳本和控件ID有關的其他問題(因爲現在所有字段都以「PageContent_」爲前綴)。我在整個應用程序的幾個不同位置使用了相同的編輯器模板技術,其他人都沒有在ViewModel上展示這種奇怪的依賴關係。

有沒有其他人遇到過類似的問題?

編輯視圖

<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/Admin/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<PageContent>" %> 

<% using (Html.BeginForm()) 
    { %> 
<%=Html.Hidden("PageID", Model.Page.ID) %> 
<%=Html.EditorFor(p => p)%> 
<input type="submit" name="btnSave" value="Save" /> 
<input type="submit" name="btnCancel" value="Cancel" class="cancel" /> 
<% } 

操作(工作)

[HttpPost, ValidateInput(false)] 
public ActionResult EditContent(int id, FormCollection formCollection) {} 

添加視圖

<%@ Page Title="" Language="C#" MasterPageFile="~/Areas/Admin/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<PageContent>" %> 

<% using (Html.BeginForm()) 
    { %> 
<%=Html.Hidden("PageID", ViewData["PageID"]) %> 
<%=Html.EditorFor(p => p)%> 
<input type="submit" name="btnSave" value="Save" /> 
<input type="submit" name="btnCancel" value="Cancel" class="cancel" /> 
<% } %> 

動作(不及格)

// content is ALWAYS null 
[HttpPost, ValidateInput(false)] 
public ActionResult AddContent(PageContent content, FormCollection formCollection) {} 

之前你哭 「重複」

這個問題確實涉及到this one,但這個問題的目的是針對具體的問題我體驗,而不是在那裏提出更普遍的問題。

+0

嘗試刪除「FormCollection」。這不是必需的。 – 2009-11-30 18:03:35

+0

如果我錯了,請更正我的錯誤,但是您是否需要前綴在默認模型聯編程序中工作?還是你忘記在參數上設置綁定屬性? – Min 2009-11-30 18:04:41

+0

@Daniel:我使用FormCollection來檢查被點擊的「取消」按鈕以及一些額外的信息,這些信息都在模型之外。我幾乎在每一次添加/編輯操作中都一直使用相同的簽名而不會失敗。 – 2009-11-30 18:26:19

回答

14

我跟蹤了這​​個問題,這是一個相當有趣的問題。

當DefaultModelBinder嘗試解析模型項時,它所做的第一件事情之一是檢查數據被綁定中是否有前綴字段;它通過檢查任何以模型對象的名稱開頭的表單項來完成此操作(如果你問我的話,這看起來好像是極其任意的)。如果找到任何「前綴」字段,則會導致調用不同的綁定邏輯。

ASP.NET MVC 2預覽版2 BindModel()來源

public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { 
    if (bindingContext == null) { 
     throw new ArgumentNullException("bindingContext"); 
    } 
    bool performedFallback = false; 
    if (!String.IsNullOrEmpty(bindingContext.ModelName) && !DictionaryHelpers.DoesAnyKeyHavePrefix(bindingContext.ValueProvider, bindingContext.ModelName)) { 
     // We couldn't find any entry that began with the prefix. If this is the top-level element, fall back 
     // to the empty prefix. 
     if (bindingContext.FallbackToEmptyPrefix) { 
      /* omitted for brevity */ 
      }; 
      performedFallback = true; 
     } 
     else { 
      return null; 
     } 
    } 

    // Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string)) 
    // or by seeing if a value in the request exactly matches the name of the model we're binding. 
    // Complex type = everything else. 
    if (!performedFallback) { 
     /* omitted for brevity */ 
    } 
    if (!bindingContext.ModelMetadata.IsComplexType) { 
     return null; 
    } 
    return BindComplexModel(controllerContext, bindingContext); 
} 

我定義來處理添加操作的控制器動作定義所謂的「內容」,並在我的域名PageContent一個PageContent項目都有一個屬性所謂的「內容」與「內容」的模型名稱「匹配」,從而導致DefaultModelBinder假定我有一個前綴值,實際上它只是PageContent的成員。通過從改變簽名工具

[HttpPost, ValidateInput(false)] 
public ActionResult AddContent(PageContent content, FormCollection formCollection) {} 

到:

[HttpPost, ValidateInput(false)] 
public ActionResult AddContent(PageContent pageContent, FormCollection formCollection) {} 

的DefaultModelBinder再一次能夠正確地綁定到我的PageContent模型項目。我不確定編輯視圖爲什麼不顯示這種行爲,但是我追蹤了問題的根源。

在我看來,這個問題非常接近「bug」狀態。這是有道理的,我的看法最初與ViewModel工作,因爲「內容」前綴「PageContent_」,但像這樣的核心框架功能/錯誤不應該是未解決的恕我直言。

+1

公約的性質是任意的。我想我有一個與MVC類似的問題,所以我三重檢查名稱和前綴。這嚴重地讓我想要能夠使用Linq的一切。 – Min 2009-11-30 21:37:49

+1

當你保證沒有人會得到它時,我誠實地說,當我要求你發佈PageContent類的構成時,這種情況實際上經歷了我的頭腦;-)無論如何,很高興你知道了它:-) – Charlino 2009-11-30 22:36:06

+0

@Charlino藐視期望的方式haha – 2009-11-30 23:39:07