2011-10-04 73 views
31

Tuve este problema una vez y no lo resolví. Tengo una lista (generados en un controlador MVC3):'objeto' no contiene una definición para 'X'

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

y en mi página (Razor) Trato de iterar a través de él:

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

pero la referencia a o.EnglishName falla con el error:

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

aunque lo curioso es que si escribo en la ventana Inmediato (mientras depuración):

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

por lo que obviamente el campo está ahí. ¿Cuál es mi problema aquí?

Respuesta

58

Está utilizando un objeto anónimo aquí:

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

objetos anónimos se emiten como internal por el compilador. Las vistas de Razor se compilan automáticamente en un ensamblaje separado por el tiempo de ejecución de ASP.NET. Esto significa que no puede acceder a ningún objeto anónimo generado en sus controladores.

Así que con el fin de solucionar el problema se podría definir una vista de modelo:

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

y luego en el uso del controlador esta vista del modelo:

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

Y ahora que tiene un modelo de vista la siguiente mejora de su código es, por supuesto, deshacerse de esta basura de ViewBag que estoy harto de ver y simplemente usar modelos de vista y mecanografía fuerte:

y luego, por supuesto, tener una visión inflexible:

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

y luego definir la plantilla de visualización correspondiente, que automáticamente se dictó por el motor ASP.NET MVC para cada elemento del modelo de vista para que Don Ni siquiera necesidad de escribir una sola foreach en sus puntos de vista (~/Views/Shared/DisplayTemplates/LanguageViewModel.cshtml):

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

wow. respuesta increíble (+1 por eso). muchas gracias, y sí, estoy en el proceso de eliminar todas las referencias al ViewBag a favor de los modelos de vista ... – ekkis

+1

muy bien, pero a veces podemos necesitar ViewBags. Solo podemos pasar un modelo a una vista. Sin embargo, es posible que deseemos enviar otra recopilación de datos pequeños, ¿no? y en este caso, es más fácil enviar esa pequeña recopilación de datos con ViewBags. Por supuesto, también puedes enviarlo con PartialViews. No sé si hay otras recomendaciones. – oneNiceFriend

4

esto conducía ahórrame hasta que me fui a mi código y encontré esto:

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

Si lo cambia a:

public class AdsViewModel 

arreglaron.

-1

Puede ocurrir también si el nombre de su clase no coincide con el nombre del archivo (sé que es estúpido, pero podría ayudarle)

0

Utilice ViewData en lugar de ViewBag gustaría.

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

Entonces

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