ASP.NET MVC - Form возвращает нулевую модель, если только модель не заключена в пользовательскую ViewModel

StackOverflow https://stackoverflow.com/questions/1821302

Вопрос

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

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

Прежде чем ты закричишь: "дублируй".

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

Это было полезно?

Решение

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

Когда 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 с именем "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_", но подобная основная функция / ошибка фреймворка не должна оставаться без внимания, ИМХО.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top