2011-10-04 16 views
31

Ho avuto questo problema una volta e non l'ho risolto. Ho una lista (generato in un controller MVC3):'oggetto' non contiene una definizione per 'X'

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

e sulla mia pagina (Razor) cerco di scorrere attraverso di essa:

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

ma il riferimento a o.EnglishName fallisce con l'errore:

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

anche se la cosa curiosa è che se digito nella finestra immediata (mentre il debug):

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

quindi ovviamente il campo è lì. Qual è il mio problema qui?

risposta

58

Si sta utilizzando un oggetto anonimo qui:

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

oggetti anonimi vengono emessi come internal dal compilatore. Le viste Razor vengono automaticamente compilate in un assembly separato dal runtime ASP.NET. Ciò significa che non è possibile accedere a oggetti anonimi generati nei controller.

Quindi, al fine di risolvere il problema che si potrebbe definire un modello di vista:

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

e poi nel vostro uso del controller questo punto di vista il modello:

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

E ora che si dispone di un modello di vista il prossimo miglioramento del tuo codice è ovviamente quello di sbarazzarmi di questa cagata di ViewBag che sono stufo di vedere e utilizzare semplicemente i modelli di visualizzazione e la tipizzazione forte:

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

e poi, naturalmente, avere una visione fortemente tipizzato:

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

e quindi definire il modello di visualizzazione corrispondente, che sarà automaticamente reso dal motore di ASP.NET MVC per ogni elemento del modello vista in modo che tu don 't nemmeno bisogno di scrivere una sola foreach nelle viste (~/Views/Shared/DisplayTemplates/LanguageViewModel.cshtml):

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

wow. risposta fantastica (+1 per quello). grazie mille - e sì, sono in procinto di rimuovere tutti i riferimenti a ViewBag in favore di modelli di viste ... – ekkis

+1

molto bene, ma a volte potremmo aver bisogno di ViewBag. Possiamo solo passare un modello a una vista. Tuttavia, potremmo voler inviare qualche altra piccola raccolta di dati, no? e in questo caso, è più facile inviare quella piccola raccolta di dati con ViewBags. Ovviamente puoi inviarlo anche con PartialViews. Non so ci sono altri consigli. – oneNiceFriend

4

questo mi stava facendo ricambio fino a quando ho controllato il mio codice e abbiamo trovato questo:

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

Modifica a:

public class AdsViewModel 

riparato.

-1

Può capitare così se il nome della vostra classe non corrisponde al nome del file (lo so che è stupido ma potrebbe aiutare)

0

Si prega di usare Viewdata invece di ViewBag piace.

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

Poi

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