web-dev-qa-db-fra.com

Classeur d'objets complexes et de modèles ASP.NET MVC

J'ai une structure d'objet de modèle avec une classe Foo qui contient une Bar avec une valeur de chaîne.

public class Foo
{
    public Bar Bar;
}

public class Bar
{
    public string Value { get; set; }
}

Et un modèle de vue qui utilise cette structure comme celle-ci

public class HomeModel
{
    public Foo Foo;
}

J'ai alors un formulaire en vue qui dans Razor ressemble à ceci.

<body>
    <div>
        @using (Html.BeginForm("Save", "Home", FormMethod.Post))
        {
            <fieldset>
                @Html.TextBoxFor(m => m.Foo.Bar.Value)
                <input type="submit" value="Send"/>
            </fieldset>
        }

    </div>
</body>

En html ça devient.

<form action="/Home/Save" method="post">
    <fieldset>
        <input id="Foo_Bar_Value" name="Foo.Bar.Value" type="text" value="Test">
        <input type="submit" value="Send">
    </fieldset>
</form>

Enfin, le contrôleur pour gérer les post-loos comme ceci

[HttpPost]
public ActionResult Save(Foo foo)
{
    // Magic happends here
    return RedirectToAction("Index");
}

Mon problème est que Bar dans Foo est null une fois que l'action Save est effectuée (Foo est créé mais avec un champ nullBar).

Je pensais que le classeur de modèle dans MVC serait capable de créer les objets Foo et Bar et de définir la propriété Value tant qu'elle ressemble à ce qui est décrit ci-dessus. Qu'est-ce que je rate?

Je sais aussi que mon modèle de vue est un peu trop compliqué et pourrait être plus simple, mais je voudrais vraiment m'aider si je pouvais utiliser la structure d'objet plus profonde. Les exemples ci-dessus utilisent ASP.NET 5.

28
Riri

Premièrement, la variable DefaultModelBinder ne sera pas liée aux champs, vous devez donc utiliser des propriétés.

public class HomeModel
{
  public Foo Foo { get; set; }
}

Deuxièmement, les assistants génèrent des contrôles basés sur HomeModel mais vous enregistrez à nouveau vers Foo. Changez la méthode POST pour 

[HttpPost]
public ActionResult Save(HomeModel model)

ou utilisez la variable BindAttribute pour spécifier la variable Prefix (qui supprime essentiellement la valeur du préfixe des valeurs publiées - pour que Foo.Bar.Value devienne Bar.Value aux fins de la liaison)

[HttpPost]
public ActionResult Save([Bind(Prefix="Foo")]Foo model)

Notez également que vous ne devez pas nommer le paramètre de méthode avec le même nom que l'une de vos propriétés, sinon la liaison échouera et votre modèle sera null.

36
user3559349

Je viens de découvrir une autre raison qui peut arriver, c'est-à-dire si votre propriété est nommée Settings! Considérez le modèle de vue suivant:

public class SomeVM
{
    public SomeSettings DSettings { get; set; } // named this way it will work

    public SomeSettings Settings { get; set; } // property named 'Settings' won't bind!

    public bool ResetToDefault { get; set; }
}

Dans le code, si vous vous liez à la propriété Settings, la liaison n'est pas établie (pas seulement lors de la publication, mais même lors de la génération du formulaire). Si vous renommez Settings en DSettings (etc.), cela fonctionne à nouveau soudainement.

3
Nicholas Petersen

J'avais le même problème et après avoir suivi les étapes de @Stephen Muecke, j'ai réalisé que le problème était dû à la désactivation de mes entrées (je les désactivais avec JQuery sur document prêt), comme vous pouvez le voir ici: Comment envoyer des informations désactivées entrée dans ASP.NET MVC? . À la fin, j'ai utilisé l'attribut désactivé en lecture seule au lieu de l'attribut désactivé et toutes les valeurs ont été envoyées avec succès au contrôleur.

0
joalcego

J'ai eu le même problème, mais une fois que j'ai créé un HIDDEN FIELD pour la clé étrangère ... tout a bien fonctionné ...

EXEMPLE DE FORME: 

@using (Html.BeginForm("save", "meter", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    @Html.HiddenFor(model => Model.Entity.Id)
    @Html.HiddenFor(model => Model.Entity.DifferentialMeter.MeterId)
    @Html.HiddenFor(model => Model.Entity.LinearMeter.MeterId)
    @Html.HiddenFor(model => Model.Entity.GatheringMeter.MeterId)

    ... all your awesome controls go here ...
}

EXEMPLE D'ACTION: 

// POST: /Meter/Save
[HttpPost]
public ActionResult Save(Meter entity)
{
    ... world-saving & amazing logic goes here ...
}

JOLIES IMAGES: 
 enter image description here

0
Prisoner ZERO