2011-10-04 9 views
31

Raz miałem ten problem i go nie rozwiązałem. Mam listę (wygenerowany w sterowniku MVC3):"Obiekt" nie zawiera definicji "X"

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

i na mojej stronie (Razor) Staram się iterację go:

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

ale odniesienie do o.EnglishName niepowodzeniem z błędem:

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

choć Ciekawostką jest, że gdybym wpisać w oknie Immediate (podczas debugowania):

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

tak oczywiście pole istnieje. Jaki jest mój problem?

Odpowiedz

58

Używasz anonimowego obiektu tutaj:

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

anonimowych obiekty są emitowane jako internal przez kompilator. Widoki Razor są automatycznie kompilowane do osobnego zestawu przez środowisko wykonawcze ASP.NET. Oznacza to, że nie masz dostępu do żadnych anonimowych obiektów wygenerowanych w kontrolerach.

Zatem w celu ustalenia problemu można zdefiniować model wyświetlania:

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

a potem w użytku kontrolera ten widok model:

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

a teraz, że masz widok modelu następnym ulepszeniem twojego kodu jest oczywiście pozbyć się tego gówna z ViewBag, że mam dość widzenia i po prostu używać modeli widoku i silnego pisania:

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

i wtedy oczywiście mieć silnie typami widok:

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

a następnie zdefiniować odpowiedni szablon wyświetlania, które zostaną automatycznie świadczonych przez silnik ASP.NET MVC dla każdego elementu modelu widoku, dzięki czemu don „t nawet trzeba pisać pojedynczy foreach w widokach (~/Views/Shared/DisplayTemplates/LanguageViewModel.cshtml):

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

wow. wspaniała odpowiedź (+1 za to). dziękuję bardzo - i tak, jestem w trakcie usuwania wszystkich odwołań do ViewBag na rzecz modeli-widoków ... – ekkis

+1

bardzo dobrze, ale czasami możemy potrzebować ViewBags. Możemy przekazać tylko jeden model do jednego widoku. Jednak możemy chcieć wysłać trochę innych małych zbiorów danych, nieprawdaż? w tym przypadku łatwiej jest wysłać tę małą kolekcję danych za pomocą ViewBags. Oczywiście możesz też przesłać go z PartialViews. Nie wiem, czy są jakieś inne zalecenia. – oneNiceFriend

4

ten doprowadza mnie oszczędzić aż sprawdziłem mój kod i znalazłem to:

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

Zmiana go:

public class AdsViewModel 

naprawił.

-1

może się zdarzyć, jak również wtedy, gdy nazwa klasy nie pasuje do nazwy pliku (wiem, że to głupie, ale to może pomóc)

0

Proszę użyć ViewData zamiast ViewBag podoba.

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

Następnie

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