2011-10-04 4 views
31

J'ai déjà eu ce problème et je ne l'ai pas résolu. J'ai une liste (générée dans un contrôleur MVC3):'objet' ne contient pas de définition pour 'X'

ViewBag.Languages = db.Languages 
    .Select(x => new { x.Name, x.EnglishName, x.Id }) 
    .ToList(); 

et sur ma page (Razor) je tente de le parcourir:

foreach (var o in ViewBag.Languages) 
{ 
    string img = "Lang/" + o.EnglishName + ".png"; 
    @* work *@ 
} 

mais la référence à o.EnglishName échoue avec l'erreur:

'object' does not contain a definition for 'EnglishName'

si la chose curieuse est que si je tape dans la fenêtre immédiate (tout débogage):

o 
{ Name = བོད་སྐད་, EnglishName = Tibetan, Id = 31 } 
    EnglishName: "Tibetan" 
    Id: 31 
    Name: "བོད་སྐད་" 

donc évidemment le champ est là. Quel est mon problème ici?

Répondre

58

Vous utilisez un objet anonyme ici:

ViewBag.Languages = db.Languages 
    .Select(x => new { x.Name, x.EnglishName, x.Id }) 
    .ToList(); 

objets anonymes sont émis comme internal par le compilateur. Les vues Razor sont automatiquement compilées dans un assembly séparé par le runtime ASP.NET. Cela signifie que vous ne pouvez accéder à aucun objet anonyme généré dans vos contrôleurs.

Ainsi, afin de résoudre votre problème, vous pouvez définir un modèle de vue:

public class LanguageViewModel 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string EnglishName { get; set; } 
} 

puis dans l'utilisation du contrôleur ce modèle de vue:

ViewBag.Languages = db.Languages 
    .Select(x => new LanguageViewModel 
    { 
     Name = x.Name, 
     EnglishName = x.EnglishName, 
     Id = x.Id 
    }) 
    .ToList(); 

Et maintenant que vous avez un modèle de vue l'amélioration suivante à votre code est bien sûr de se débarrasser de cette merde de ViewBag que je suis malade de voir et d'utiliser simplement des modèles de vue et de forte typage:

public ActionResult Foo() 
{ 
    var model = db 
     .Languages 
     .Select(x => new LanguageViewModel 
     { 
      Name = x.Name, 
      EnglishName = x.EnglishName, 
      Id = x.Id 
     }) 
     .ToList(); 
    return View(model); 
} 

puis d'avoir bien entendu une vue fortement typé:

@model IEnumerable<LanguageViewModel> 
@Html.DisplayForModel() 

puis définir le modèle d'affichage correspondant qui sera rendu automatiquement par le moteur ASP.NET MVC pour chaque élément du modèle de vue afin que vous ne « t besoin même d'écrire une seule foreach dans votre point de vue (~/Views/Shared/DisplayTemplates/LanguageViewModel.cshtml):

@model LanguageViewModel 
... generate the image or whatever you was attempting to do in the first place 
+1

wow. réponse impressionnante (+1 pour cela). merci beaucoup - et oui, je suis en train de supprimer toutes les références à la ViewBag en faveur des modèles de vue ... – ekkis

+1

très bien, mais nous pouvons avoir besoin de ViewBags parfois. Nous ne pouvons transmettre qu'un seul modèle à une seule vue. Cependant, nous pourrions vouloir envoyer d'autres petites collectes de données, n'est-ce pas? et dans ce cas, il est plus facile d'envoyer cette petite collection de données avec ViewBags. Bien sûr, vous pouvez également l'envoyer avec les vues partielles. Je ne sais pas s'il y a d'autres recommandations. – oneNiceFriend

4

cela me conduisait épargnerai jusqu'à ce que j'ai vérifié mon code et trouvé ceci:

class AdsViewModel 
{ 
    public int Id { get; set; } 
    public string City { get; set; } 
    public string CompanyName { get; set; } 
    public string ContactName { get; set; } 
    public string UserEmail { get; set; } 
    public string ContactPhone { get; set; } 
    public string ShortTitle { get; set; } 
    public string AdUrl { get; set; } 
} 

Changer à:

public class AdsViewModel 

le fixe.

-1

Il peut arriver aussi bien si le nom de votre classe ne correspond pas au nom du fichier (je sais que c'est stupide, mais il pourrait vous aider)

0

S'il vous plaît utiliser ViewData au lieu de ViewBag comme.

ViewData["Lang"] = db.Languages 
.Select(x => new { x.Name, x.EnglishName, x.Id }) 
.ToList(); 

Puis

foreach (var o in (dynamic) ViewData["Lang"]) 
{ 
string img = "Lang/" + o.EnglishName + ".png"; 
@* work *@ 
} 
Questions connexes