ASP.NET MVC - Il modulo restituisce un modello nullo a meno che il modello non sia avvolto in un ViewModel personalizzato

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

Domanda

Ho un paio di viste nella mia applicazione che mostrano entrambe lo stesso modello di editor per uno dei miei elementi del modello; delle due viste (" Aggiungi " e " Modifica "), " Modifica " funziona bene, ma " Aggiungi " restituisce null per il modello quando l'azione del mio controller gestisce il post.

Ho scoperto che se do " Aggiungi " visualizza un ViewModel personalizzato e chiama Html.EditorFor (p = > p.PageContent) anziché semplicemente chiamare EditorFor () sull'intero oggetto Model- Html.EditorFor (p = > p) , quindi il modulo restituisce il modello corretto, non nullo, ma ciò genera altri problemi relativi ai miei ID di scripting e controllo sul lato client (poiché ora tutti i campi hanno il prefisso " PageContent_ "). Sto usando la stessa tecnica del modello dell'editor in diversi punti della mia applicazione e nessuno degli altri mostra questa strana dipendenza da un ViewModel.

Qualcun altro ha mai avuto problemi simili?

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

Azione (funzionante)

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

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

Azione (non riuscita)

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

Prima di piangere " duplicare "

Questa domanda riguarda questa , ma questa domanda è destinato a risolvere il problema specifico che sto riscontrando piuttosto che la domanda più generale posta lì.

È stato utile?

Soluzione

Ho rintracciato il problema ed è piuttosto interessante.

Quando DefaultModelBinder tenta di risolvere un elemento del modello, una delle prime cose che fa è controllare se ci sono campi prefissati nei dati che vengono associati; lo fa controllando tutti gli elementi del modulo che iniziano con il nome dell'oggetto modello (questo sembra estremamente arbitrario , se me lo chiedi). Se presente " prefisso " i campi vengono trovati, quindi viene invocata una logica di associazione diversa.

ASP.NET MVC 2 Preview 2 BindModel () Source

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

L'azione del controller che ho definito per gestire l'azione Aggiungi definisce un elemento PageContent chiamato " content " e nel mio dominio PageContent ha una proprietà chiamata " Contenuto " quale "quotato" " con il nome del modello di " contenuto " inducendo quindi DefaultModelBinder ad assumere un valore prefissato quando in realtà era semplicemente un membro di PageContent. Modificando la firma-

Da:

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

a:

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

DefaultModelBinder è stato ancora una volta in grado di collegarsi correttamente all'elemento del mio modello PageContent. Non sono sicuro del perché anche la vista Modifica non abbia mostrato questo comportamento, ma in entrambi i casi ho rintracciato l'origine del problema.

Mi sembra che questo problema sia molto vicino a " bug " stato. È logico che la mia vista abbia funzionato inizialmente con ViewModel perché "content" stava ottenendo il prefisso con " PageContent_ " ;, ma una funzione / bug del framework di base come questo non dovrebbe essere indirizzato IMHO.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top