ASP.NET MVC - 表单返回空模型,除非模型包含在自定义ViewModel中

Nat*_*lor 4 asp.net-mvc model-binding viewmodel

我的应用程序中有一对视图,它们都为我的一个模型项显示相同的编辑器模板; 在两个视图("添加"和"编辑")中,"编辑"工作正常,但当我的控制器操作处理帖子时,"添加"为模型返回null.

我发现如果我给"添加"视图一个自定义的ViewModel并调用Html.EditorFor(p => p.PageContent)而不是简单地在整个Model对象上调用EditorFor()Html.EditorFor(p => p),那么表单将返回正确的非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" />
<% }
Run Code Online (Sandbox Code Playgroud)

行动(工作)

[HttpPost, ValidateInput(false)]
public ActionResult EditContent(int id, FormCollection formCollection) {}
Run Code Online (Sandbox Code Playgroud)

添加视图

<%@ 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" />
<% } %>
Run Code Online (Sandbox Code Playgroud)

行动(失败)

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

在你哭之前"复制"

这个问题确实涉及到这一块,但是这个问题的目的是针对我遇到,而不是要求有更普遍的问题,具体的问题.

Nat*_*lor 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);
}
Run Code Online (Sandbox Code Playgroud)

我定义的用于处理Add操作的控制器操作定义了一个名为"content"的PageContent项,在我的域中PageContent有一个名为"Content"的属性,它与"内容"的模型名称"匹配",从而导致DefaultModelBinder假设我有实际上它只是PageContent的一个成员的前缀值.通过改变签名 -

从:

[HttpPost, ValidateInput(false)]
public ActionResult AddContent(PageContent content, FormCollection formCollection) {}
Run Code Online (Sandbox Code Playgroud)

至:

[HttpPost, ValidateInput(false)]
public ActionResult AddContent(PageContent pageContent, FormCollection formCollection) {}
Run Code Online (Sandbox Code Playgroud)

DefaultModelBinder再次能够正确绑定到我的PageContent模型项.我不确定为什么编辑视图也没有显示这种行为,但无论哪种方式我都追查了问题的根源.

在我看来,这个问题非常接近"bug"状态.我的视图最初与ViewModel一起工作是有道理的,因为"内容"的前缀是"PageContent_",但是像这样的核心框架功能/ bug应该不是没有得到解决的恕我直言.