ASP.NET MVC: el formulario devuelve un modelo nulo a menos que el modelo se envuelva en un modelo de vista personalizado

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

Pregunta

Tengo un par de vistas en mi aplicación que muestran la misma plantilla de editor para uno de mis elementos de modelo; de las dos vistas (" Añadir " y " Editar "), " Editar " funciona bien, pero "Añadir" devuelve nulo para el modelo cuando mi acción de controlador maneja la publicación.

Descubrí que si doy el " Agregar " ver un ViewModel personalizado y llamar a Html.EditorFor (p = > p.PageContent) en lugar de simplemente llamar a EditorFor () en todo el objeto Modelo- Html.EditorFor (p = > p) , luego el formulario devuelve el modelo correcto, no nulo, pero eso genera otros problemas relacionados con mis scripts del lado del cliente y las ID de control (ya que ahora todos los campos tienen el prefijo "Contenido de la página"). Estoy usando la misma técnica de plantilla de editor en algunos lugares diferentes a lo largo de mi aplicación y ninguno de los otros exhibe esta extraña dependencia de un modelo de vista.

¿Alguien más ha experimentado problemas similares?

Editar vista

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

Acción (de trabajo)

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

Agregar vista

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

Acción (error)

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

Antes de llorar " duplicar "

Esta pregunta se relaciona con esta , pero esta pregunta está destinado a abordar el problema específico que estoy experimentando en lugar de la pregunta más general que se hace allí.

¿Fue útil?

Solución

Seguí el problema y es bastante interesante.

Cuando DefaultModelBinder intenta resolver un elemento del modelo, una de las primeras cosas que hace es verificar si hay campos prefijados en los datos que se están vinculando; lo hace comprobando si hay elementos de formulario que comiencen con el nombre del objeto modelo (esto parece extremadamente arbitrario , si me pregunta). Si hay " prefijado " los campos se encuentran y, a continuación, se invoca una lógica de enlace diferente.

ASP.NET MVC 2 Preview 2 BindModel () Fuente

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

La acción del controlador que definí para manejar la acción Agregar define un elemento PageContent llamado "contenido". y en mi dominio PageContent tiene una propiedad llamada " Contenido " que " coinciden " con el nombre del modelo de "contenido" por lo tanto, DefaultModelBinder asume que tenía un valor prefijado cuando en realidad era simplemente un miembro de PageContent. Al cambiar la firma-

desde :

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

to:

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

El DefaultModelBinder pudo una vez más vincularse correctamente a mi elemento de modelo de PageContent. No estoy seguro de por qué la vista Editar tampoco mostró este comportamiento, pero de cualquier manera he rastreado la fuente del problema.

Me parece que este problema está muy cerca de "error" estado. Tiene sentido que mi vista funcionó inicialmente con ViewModel porque " content " estaba siendo prefijado con "Contenido de la página", pero una característica / error del marco central como este no debería ser abordado en mi humilde opinión.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top