Modèle dynamique MVC Razor, 'objet' ne contient pas de définition pour 'PropertyName'

106

Utilisation de MVC 3 avec le moteur de visualisation Razor. J'ai cette vue:

@model dynamic
@{
    var products = (List<ListItemBaseModel>)Model.Products;
    var threshold = (int)(Model.Threshold ?? 1);
    var id = Guid.NewGuid().ToString();
}

Il est appelé depuis une autre vue en utilisant ce code:

@Html.Partial("PartialViewName", new { Products = Model, Threshold = 5 })

Dans les deux vues, lorsque je les débogue et regarde Model, il semble contenir le bon objet. Lorsque j'exécute le code, j'obtiens une erreur sur la ligne "var products =" disant:

«objet» ne contient pas de définition pour «produits»

Quelqu'un peut-il m'expliquer pourquoi j'obtiens cette erreur? Encore une fois, lorsque je regarde l'objet Model en mode débogage, il semble correct (ayant 2 propriétés: Produits et Seuil)

Ruud van Falier
la source

Réponses:

150

Passez-vous une instance d'une classe anonyme comme modèle de vue? Je viens d'essayer ceci (modèle de vue dynamique en CSHTML) et j'ai eu la même erreur que le vôtre lors de l'utilisation d'une classe anonyme, mais cela fonctionnait bien si je créais une classe nommée. J'ai cherché mais je n'ai vu cela documenté nulle part.

// error
return View(new { Foo = 1, Bar = "test" });

// worked
return View(new TestClass { Foo = 1, Bar = "test" });

EDIT # 1:

Selon David Ebbo , vous ne pouvez pas passer un type anonyme dans une vue typée dynamiquement car les types anonymes sont compilés comme internal. La vue CSHTML étant compilée dans un assembly séparé, elle ne peut pas accéder aux propriétés du type anonyme.

EDIT # 2:

David Ebbo a édité son message avec cette clarification:

Remarque (22/12/2011): maintenant que MVC 3 prend directement en charge la dynamique, la technique ci-dessous n'est plus nécessaire. Ce post est en fait ce qui a conduit à intégrer la fonctionnalité dans MVC!

Lucas
la source
1
Le montage est agréable à savoir. J'ai juste eu le même problème et je n'ai pas compris le WTF là-bas. Merci pour l'explication.
Yanick Rochon
18
EDIT # 2 suggère que maintenant (MVC> 3) est possible de faire la ligne marquée avec "erreur"? return View(new { Foo = 1, Bar = "test" });? Parce que j'utilise MVC 4 et que j'obtiens toujours "l'objet ne contient pas de définition pour Foo"
sports
@sporte moi aussi ... avez-vous trouvé une solution de contournement? (à côté de l' ToExpandoun)
Alex
2
Alors maintenant, en 2018, en utilisant les vues ASP.NET Core 2.1 et Razor, je trouve que l'erreur dans la question d'origine me mord toujours. Donc, je ne sais pas de quoi parle ce discours sur la résolution de MVC 3, car il semble toujours cassé.
Andrew Arnott
41

Sur .NET 4.0, les types anonymes peuvent facilement être convertis en ExpandoObjects et ainsi tous les problèmes sont résolus avec la surcharge de la conversion elle-même. Découvrez ici

Adaptabi
la source
Je vous en prie. Peut-être que cela donne un coup de pied à M $ pour rendre les types anonymes plus utilisables
Adaptabi
Cela s'applique-t-il aux partiels? J'ai eu une erreur indiquant que les Partials ne peuvent pas être distribués dynamiquement ...
John Bubriski
1
Quels partiels? Pouvez vous donner un exemple?
Adaptabi
27

Cela n'a rien à voir avec les types anonymes ayant des propriétés internes

Il est parfaitement possible de passer des types anonymes d'une vue à une vue partielle

J'ai rencontré le même problème aujourd'hui et cela n'avait rien (directement) à voir avec le problème du passage des types anonymes et de leurs internalpropriétés inhérentes .

En tant que tel, en ce qui concerne la question des OP, la réponse de @Lucas n'est pas pertinente - même si la solution de contournement fonctionnera .

Dans la question OP, un type anonyme est passé d'une vue dans l'assembly X à une vue partielle dans l'assembly X , par conséquent le problème que David Ebbo a souligné concernant les propriétés internes pour les types anonymes est sans conséquence; les types compilés pour la vue, le type partiel et le type anonyme sont tous contenus dans le même assembly .

Alors, qu'est-ce qui cause l'échec soudain de passer un type anonyme d'une vue à un partiel?

Au moins dans ma situation, j'ai découvert que c'était dû à une autre vue dans le même dossier qui spécifie un type de modèle qui ne peut pas être résolu . Les vues sont compilées au moment de l'exécution, et il serait donc logique qu'un échec lors de l'exécution de la compilation des vues signifierait également un échec de compilation des types dynamiques et que le partiel recevrait simplement un fichier object. Ce qui se passe n'est pas immédiatement évident, mais dans l'exemple spécifique des OP (et le mien), c'est plus que probablement la cause du problème.

Il est intéressant de noter que si le type de modèle est correct mais qu'une autre partie de la vue ne se compile pas, les types anonymes ne sont pas affectés de la même manière. Cela doit être dû à la façon dont Razor rompt la compilation dynamique des composants de la vue.

Une fois que vous avez corrigé la vue incriminée, reconstruisez toute la solution ou nettoyez et reconstruisez le projet avant de vérifier si elle est corrigée.

Pour vous assurer de ne pas être à nouveau pris au piège, vous pouvez activer la compilation au moment de la compilation de vos vues Razor en ajoutant ceci à votre csprojfichier:

<PropertyGroup>
    <MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
Joshcomley
la source
2
Cela a résolu mon problème - l'utilisation de "@model dynamic" semblait au départ être la bonne solution, mais me conduisait en fait sur la mauvaise voie.
crimbo
J'ai nettoyé la solution, l'ai reconstruite et l'erreur a disparu .. 121 votes en hausse perdus.
maxbeaudoin
J'ai mis à jour ma réponse pour refléter le support de MVC pour les modèles de vue dynamique depuis MVC 3.
Lucas
L'activation de la compilation de vues de temps en temps est toujours utile pour une énorme base de code. Révèle toutes sortes de problèmes, fautes de frappe, erreurs avec T4MVC grâce au typage fort qu'il a introduit, etc.
Denis The Menace
Oh, c'est vrai: je viens de remarquer que nous parlons ici de passer d'une vue à une vue partielle. Pas d'un contrôleur à une vue, ce qui est mon problème.
mwardm
9

Ajoutez la classe suivante n'importe où dans votre solution (utilisez l'espace de noms System, donc c'est prêt à être utilisé sans avoir à ajouter de références) -

    namespace System
    {
        public static class ExpandoHelper
        {
            public static ExpandoObject ToExpando(this object anonymousObject)
            {
                IDictionary<string, object> anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousObject);
                IDictionary<string, object> expando = new ExpandoObject();
                foreach (var item in anonymousDictionary)
                    expando.Add(item);
                return (ExpandoObject)expando;
            }

        }
    }

Lorsque vous envoyez le modèle à la vue, convertissez-le en Expando:

    return View(new {x=4, y=6}.ToExpando());
Segev -CJ- Shmueli
la source
1
me semble une surcharge inutile de créer d'abord un objet dynamique, puis de créer un ExpandoObject ... Il suffit de créer le ExpandoObject à la place ..
Baz1nga
@ Baz1nga Vous ne pouvez pas faire ... new Expando () {prop = value, ...}, ce qui le rend problématique. J'utilise JObject de Json.Net pour un usage similaire.
Tracker1
3
Il se sent mal d'avoir HtmlHelper là-dedans ... public static ExpandoObject ToExpando (cet objet o) {IDictionary <string, object> expando = new ExpandoObject (); foreach (var propertyInfo dans o.GetType (). GetProperties ()) {expando.Add (new KeyValuePair <string, object> (propertyInfo.Name, propertyInfo.GetValue (o, index: null))); } return (ExpandoObject) expando; }
erlando
6

Au lieu d'utiliser le dynamictype de modèle dans la vue partielle.

Vous pouvez appeler les attributs d'objets anonymes en utilisant @ViewData.Eval("foo")au lieu de @Model.foo.

Ensuite, vous pouvez supprimer @Model dynamicde la vue.

Je suis tombé sur ce problème récemment en passant certains attributs entre les vues pour l'intégration des commentaires sociaux Facebook. Exemple de code:

Html.RenderPartial(@"Layouts/Partials/_Comments", new {currentUrl = Model.CurrentPage.GetAbsoluteUrl(), commentCount = 5 });

Ensuite, à mon avis, je viens d'avoir cette div:

<div class="fb-comments" data-href="@ViewData.Eval("currentUrl")" data-numposts="@ViewData.Eval("commentCount")" data-width="100%"></div>
JamesG
la source
0

Je ne suis pas sûr que vous obteniez cette erreur car vous n'implémentez pas la solution de contournement. J'ai eu la même erreur dans une vue partielle. la solution consistait simplement à nettoyer la version et à la reconstruire. si la syntaxe est correcte, le code devrait fonctionner, mais le moteur de rasoir peut ne pas mettre à jour correctement les modifications de code.

goran
la source
0

J'ai contourné ce problème en utilisant un dictionnaire.

 @Html.Partial("_Partial", new Dictionary<string, string> { { "Key1", "Val1" }, { "Key2", "Val2" }, { "Key3", "Val3" } });
Gerade Geldenhuys
la source
-6

Pour utiliser le dynamictype, vous devez référencer l' Microsoft.CSharpassembly

the_joric
la source