ASP.NET MVC - форма возвращает нулевую модель, если модель не переносится в пользовательскую модель представления - PullRequest
4 голосов
/ 30 ноября 2009

В моем приложении есть пара представлений, которые отображают один и тот же шаблон редактора для одного из элементов моей модели; из двух представлений («Добавить» и «Редактировать») «Правка» работает нормально, но «Добавить» возвращает ноль для модели, когда действие моего контроллера обрабатывает сообщение.

Я обнаружил, что если я дам представлению «Добавить» собственный ViewModel и вызову Html.EditorFor(p => p.PageContent), а не просто вызову EditorFor () для всего объекта Model - Html.EditorFor(p => p), то форма вернет правильный, ненулевой модели, но это порождает другие проблемы, относящиеся к моим клиентским сценариям и идентификаторам элементов управления (так как теперь все поля имеют префикс «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) {}

Перед тем как плакать "дубликат"

Этот вопрос относится к этому , но этот вопрос предназначен для решения конкретной проблемы, с которой я сталкиваюсь, а не для более общего вопроса, задаваемого там.

1 Ответ

14 голосов
/ 30 ноября 2009

Я разыскал проблему, и она довольно интересная.

Когда DefaultModelBinder пытается разрешить элемент модели, первое, что он делает, - проверяет, есть ли какие-либо префиксные поля в привязываемых данных; он делает это путем проверки любых элементов формы, которые начинаются с имени объекта модели (это кажется крайне произвольным , если вы спросите меня). Если найдены какие-либо «префиксные» поля, это приводит к другой логике привязки.

ASP.NET MVC 2 Preview 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);
}

Действие контроллера, которое я определил для обработки действия Add, определяет элемент PageContent, называемый «content», и в моем домене PageContent имеет свойство «Content», которое «сопоставляется» с именем модели «content», таким образом вызывая DefaultModelBinder для Предположим, у меня было префиксное значение, хотя на самом деле это был просто элемент PageContent. Изменяя подпись-

от

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

до:

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

DefaultModelBinder снова смог правильно привязаться к моему элементу модели PageContent. Я не уверен, почему представление «Правка» также не отображало это поведение, но в любом случае я нашел источник проблемы.

Мне кажется, что эта проблема очень близка к состоянию "ошибка". Имеет смысл, что мое представление изначально работало с ViewModel, потому что «content» получал префикс «PageContent_», но такая базовая функция / ошибка структуры не должна быть безадресной, IMHO.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...