"объект" не содержит определения для "X"

У меня была эта проблема однажды, и я не решил ее. У меня есть список (сгенерированный в контроллере MVC3):

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

и на своей странице (Razor) я пытаюсь перебрать его:

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

но ссылка на o.EnglishName не удается с ошибкой:

'object' не содержит определения для 'EnglishName'

хотя любопытно, что если я наберу в Immediate Window (во время отладки):

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

так очевидно, что поле здесь. В чем моя проблема здесь?

4 ответа

Решение

Вы используете анонимный объект здесь:

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

Анонимные объекты испускаются как internal компилятором. Представления Razor автоматически компилируются в отдельную сборку средой выполнения ASP.NET. Это означает, что вы не можете получить доступ к любым анонимным объектам, сгенерированным в ваших контроллерах.

Таким образом, чтобы исправить вашу проблему, вы можете определить модель представления:

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

а затем в вашем контроллере используйте эту модель представления:

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

И теперь, когда у вас есть модель представления, следующим улучшением вашего кода, конечно же, является избавление от этого дерьма ViewBag что мне надоело видеть и просто использовать модели представления и строгую типизацию:

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

и тогда, конечно, есть строго типизированное представление:

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

и затем определите соответствующий шаблон отображения, который будет автоматически отображаться механизмом ASP.NET MVC для каждого элемента модели представления, чтобы вам даже не нужно было писать один foreach в ваших представлениях (~/Views/Shared/DisplayTemplates/LanguageViewModel.cshtml):

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

Это сводило меня с ума, пока я не проверил свой код и не нашел это:

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

Меняя это на:

public class AdsViewModel 

Починил это.

Пожалуйста, используйте ViewData вместо ViewBag, как.

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

затем

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

Это также может произойти, если имя вашего класса не совпадает с именем файла (я знаю, что это глупо, но это может вам помочь)

Другие вопросы по тегам