ASP.NET MVC - Formulário Retorna Null Modelo A menos Modelo é envolto em um ViewModel personalizado

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

Pergunta

Eu tenho um par de pontos de vista no meu aplicativo que tanto exibição o mesmo Editor de modelo para um dos meus itens de modelo; dos dois pontos de vista ( "Adicionar" e "Editar"), "Editar" fino funciona, mas em "Adicionar" está retornando nulo para o modelo quando meus punhos ação do controlador do post.

Descobri que se eu der o "Adicionar" ver uma ViewModel personalizado e Html.EditorFor(p => p.PageContent) chamada em vez de simplesmente chamar o EditorFor () em todo o Modelo object- Html.EditorFor(p => p), então a forma retorna o modelo correto, não nulo, mas que gera outros problemas relacionados aos meus client-side scripting e controle de IDs (como agora todos os campos são prefixados com "PageContent_"). Eu estou usando a mesma técnica Editor de modelo em alguns lugares diferentes ao longo da minha aplicação e nenhum dos outros estão exibindo essa dependência estranho em um ViewModel.

Tem mais alguém já experimentou problemas semelhantes?

Editar Ver

<%@ 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" />
<% }

Ação (Trabalho)

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

Adicionar Ver

<%@ 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" />
<% } %>

Ação (falha)

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

Antes de chorar "duplicado"

Esta questão se relaciona com esta , mas esta questão se destina a alvejar o problema específico que estou experimentando ao invés da questão mais geral pediu lá.

Foi útil?

Solução

Eu rastreou o problema e é um tanto interessante.

Quando as tentativas DefaultModelBinder para resolver um item de modelo das primeiras coisas que ele faz é verificar para ver se há quaisquer campos prefixadas nos dados que estão sendo vinculados; ele faz isso através da verificação de todos os itens de formulário que começam com o nome do modelo de objeto (isto parece extremamente arbitrária , se você me perguntar). Se todos os campos "pré-fixados" são encontrados, em seguida, que resulta na lógica de ligação diferente que está sendo chamado.

ASP.NET MVC 2 Preview 2 bindModel () Fonte

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

A ação do controlador I definido para lidar com a ação Adicionar define um item de PageContent chamado de "conteúdo" e na minha PageContent domínio tem uma propriedade chamada "Conteúdo", que "combinado" com o nome do modelo de "conteúdo", causando assim a DefaultModelBinder para supor que eu tinha um valor prefixado quando na verdade ele era simplesmente um membro da PageContent. Ao alterar a assinatura -

:

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

para:

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

O DefaultModelBinder foi mais uma vez capaz de se ligar correctamente ao meu item de modelo PageContent. Eu não sou certo porque a visualização Edit também não exibir esse comportamento, mas de qualquer forma eu rastreou a origem do problema.

Parece-me que esta questão cai muito próximo ao status de "bug". Faz sentido que a minha visão trabalhou inicialmente com o ViewModel porque "conteúdo" estava ficando com o prefixo "PageContent_", mas uma característica quadro core / bug como esta não deve ser IMHO sem solução.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top