ASP.NET MVC - Le formulaire renvoie un modèle nul à moins que le modèle ne soit enveloppé dans un ViewModel personnalisé

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

Question

Dans mon application, une paire de vues affiche le même modèle d'édition pour l'un de mes éléments de modèle. des deux vues ("Ajouter" et "Modifier"), "Modifier" fonctionne bien, mais "Ajouter" renvoie null pour le modèle lorsque l'action de mon contrôleur gère la publication.

J'ai constaté que si je donnais la valeur "Ajouter". affichez un ViewModel personnalisé et appelez Html.EditorFor (p = > p.PageContent) plutôt que d'appeler simplement EditorFor () sur l'ensemble de l'objet Model- Html.EditorFor (p = > p) , le formulaire renvoie alors le modèle correct, non nul, mais cela génère d'autres problèmes liés à mes ID de script et de contrôle côté client (comme maintenant, tous les champs sont précédés de "PageContent_"). J'utilise la même technique de gabarit d'édition à différents endroits de mon application et aucun des autres n'expose cette dépendance étrange à un ViewModel.

Quelqu'un at-il déjà rencontré des problèmes similaires?

Afficher la vue

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

Action (travail)

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

Ajouter une vue

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

Action (échec)

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

Avant de pleurer "dupliquer"

Cette question concerne celle-ci , mais cette question est destiné à cibler le problème que je rencontre plutôt que la question plus générale qui y est posée.

Était-ce utile?

La solution

J'ai découvert le problème et il est plutôt intéressant.

Lorsque DefaultModelBinder tente de résoudre un élément de modèle, l'une des premières choses qu'il effectue est de vérifier s'il existe des champs préfixés dans les données liées. Pour ce faire, il recherche tous les éléments de formulaire commençant par le nom de l'objet modèle (cela semble extrêmement arbitraire , si vous me le demandez). Si un "quotient" " " les champs sont trouvés, il en résulte que la logique de liaison différente est appelée.

Source BindModel () ASP.NET MVC 2 Preview 2

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'action du contrôleur que j'ai définie pour gérer l'action Add définit un élément PageContent appelé "contenu". et dans mon domaine, PageContent a une propriété appelée "Contenu". qui correspondait " avec le nom de modèle de " contenu " Ainsi, DefaultModelBinder a supposé que j'avais une valeur préfixée alors qu'en fait, il s'agissait simplement d'un membre de PageContent. En changeant la signature -

à partir de:

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

à:

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

Le DefaultModelBinder a encore une fois été capable de se lier correctement à mon élément de modèle PageContent. Je ne suis pas sûr de savoir pourquoi la vue Éditer n'a pas également affiché ce comportement, mais dans tous les cas, j'ai retrouvé la source du problème.

Il me semble que ce problème est très proche du & bug; bug " statut. Il est logique que mon point de vue fonctionne initialement avec ViewModel car "contenu". commençait à recevoir le préfixe "PageContent_", mais une fonctionnalité / un bogue de base de ce type ne devrait pas être ignorée à mon humble avis.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top