Динамическая модель MVC Razor, 'объект' не содержит определения для 'PropertyName'

106

Использование MVC 3 с механизмом просмотра Razor. У меня такой вид:

@model dynamic
@{
    var products = (List<ListItemBaseModel>)Model.Products;
    var threshold = (int)(Model.Threshold ?? 1);
    var id = Guid.NewGuid().ToString();
}

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

@Html.Partial("PartialViewName", new { Products = Model, Threshold = 5 })

В обоих представлениях, когда я их отлаживаю и смотрю модель, кажется, что он содержит правильный объект. Когда я выполняю код, я получаю сообщение об ошибке в строке "var products =":

'объект' не содержит определения для 'продуктов'

Может ли кто-нибудь объяснить мне, почему я получаю эту ошибку? Опять же, когда я смотрю объект Model в режиме отладки, он выглядит нормально (имеет 2 свойства: Products и Threshold).

Рууд ван Фалиер
источник

Ответы:

150

Вы передаете экземпляр анонимного класса в качестве модели представления? Я просто попробовал это (модель динамического представления в CSHTML) и получил ту же ошибку, что и ваша, при использовании анонимного класса, но он работал нормально, если я создал именованный класс. Я искал, но нигде не видел этого документированного.

// error
return View(new { Foo = 1, Bar = "test" });

// worked
return View(new TestClass { Foo = 1, Bar = "test" });

РЕДАКТИРОВАТЬ №1:

По словам Дэвида Эббо , вы не можете передать анонимный тип в динамически типизированное представление, потому что анонимные типы компилируются как internal. Поскольку представление CSHTML скомпилировано в отдельную сборку, оно не может получить доступ к свойствам анонимного типа.

РЕДАКТИРОВАТЬ № 2:

Дэвид Эббо отредактировал свой пост следующим образом:

Примечание (22.12.2011): теперь, когда MVC 3 имеет прямую поддержку динамического, описанный ниже метод больше не нужен. Фактически, этот пост привел к интеграции этой функции в MVC!

Лукас
источник
1
Редактирование приятно знать. У меня была такая же проблема, и я не понимал там WTF. Спасибо за объяснение.
Яник Рочон
18
РЕДАКТИРОВАТЬ № 2 предполагает, что теперь (MVC> 3) можно сделать строку с пометкой «ошибка»? return View(new { Foo = 1, Bar = "test" });? Потому что я использую MVC 4 и все еще получаю «объект не содержит определения для Foo»
sports
@sports меня тоже ... ты нашел обходной путь? (рядом с ToExpandoодним)
Alex
2
Итак, теперь, в 2018 году, используя представления ASP.NET Core 2.1 и Razor, я обнаружил, что ошибка в исходном вопросе все еще меня кусает. Так что я не знаю, о чем идет речь в этом разговоре об исправлении этого в MVC 3, поскольку он все еще кажется сломанным.
Эндрю Арнотт
41

В .NET 4.0 анонимные типы можно легко преобразовать в ExpandoObjects, и, таким образом, все проблемы устраняются за счет накладных расходов на само преобразование. Проверить здесь

Adaptabi
источник
Добро пожаловать. Может быть, это подстегнет M $, чтобы сделать анонимные типы более удобными
Adaptabi
Но относится ли это к частичным? Я получил ошибку, что частичные файлы не могут быть отправлены динамически ...
Джон Бубриски
1
Какие частички? Вы можете привести пример?
Adaptabi
27

Это не имеет ничего общего с анонимными типами, имеющими внутренние свойства.

Вполне возможно передать анонимные типы из представления в частичное представление

Сегодня я столкнулся с той же проблемой, и она не имела (напрямую) ничего общего с проблемой передачи анонимных типов и присущих им internalсвойств.

Таким образом, в отношении вопроса OP, ответ @Lucas не имеет значения - даже если обходной путь будет работать .

В вопросе OP анонимный тип передается из представления в сборке X в партиал в сборке X , поэтому проблема, которую обозначил Дэвид Эббо о внутренних свойствах анонимных типов, не имеет значения; типы, скомпилированные для представления, частичный и анонимный тип - все содержатся в одной сборке .

Так что же вызывает внезапную неудачу в передаче анонимного типа из представления в частичное?

По крайней мере, в моей ситуации я обнаружил, что это произошло из-за наличия другого представления в ТАКОЙ ПАПКЕ, которое указывает тип модели, который не может быть разрешен . Представления компилируются во время выполнения, и поэтому это имело бы смысл, поскольку сбой во время выполнения компиляции представлений также означал бы сбой при компиляции динамических типов, и партиал просто получил бы файл object. Не сразу очевидно, что происходит, но в конкретном примере OP (и моем) это более чем вероятно является причиной проблемы.

Интересно отметить, что если тип модели правильный, но другая часть представления не компилируется, анонимные типы не затрагиваются таким же образом. Это должно быть связано с тем, как Razor разбивает динамическую компиляцию составных частей представления.

После исправления неправильного представления либо перестройте все решение, либо очистите и перестройте проект, прежде чем проверять, исправлено ли оно.

Чтобы убедиться, что вас это снова не поймает, вы можете включить компиляцию ваших представлений Razor во время компиляции, добавив это в свой csprojфайл:

<PropertyGroup>
    <MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
Джошкомли
источник
2
Это устранило мою проблему - использование «@model dynamic» поначалу казалось правильным решением, но на самом деле привело меня по неправильному пути.
crimbo
Я почистил решение, перестроил его, и ошибка исчезла .. 121 голос за неуместен.
maxbeaudoin
Я обновил свой ответ, чтобы отразить поддержку MVC для моделей динамического представления, начиная с MVC 3.
Лукас
Включение компиляции представлений время от времени всегда полезно для огромной базы кода. Выявляет всевозможные проблемы, опечатки, ошибки с T4MVC благодаря введенной строгой типизации и т. Д.
Денис The Menace
А, верно: я только что заметил, что мы говорим о переходе от представления к частичному. Не от контроллера к представлению, что является моей проблемой.
mwardm 01
9

Добавьте следующий класс в любое место вашего решения (используйте пространство имен System, чтобы его можно было использовать без добавления каких-либо ссылок) -

    namespace System
    {
        public static class ExpandoHelper
        {
            public static ExpandoObject ToExpando(this object anonymousObject)
            {
                IDictionary<string, object> anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousObject);
                IDictionary<string, object> expando = new ExpandoObject();
                foreach (var item in anonymousDictionary)
                    expando.Add(item);
                return (ExpandoObject)expando;
            }

        }
    }

Когда вы отправляете модель на вид, конвертируйте ее в Expando:

    return View(new {x=4, y=6}.ToExpando());
Сегев -CJ- Шмуели
источник
1
Мне кажется, что создание сначала динамического объекта, а затем создания ExpandoObject кажется мне ненужными накладными расходами ... Вместо этого просто создайте ExpandoObject ..
Baz1nga
@ Baz1nga Вы не можете сделать ... new Expando () {prop = value, ...}, что делает его проблематичным. Я использую JObject Json.Net для аналогичного использования.
Tracker1
3
Кажется неправильным иметь там HtmlHelper ... public static ExpandoObject ToExpando (этот объект o) {IDictionary <string, object> expando = new ExpandoObject (); foreach (var propertyInfo в o.GetType (). GetProperties ()) {expando.Add (новый KeyValuePair <строка, объект> (propertyInfo.Name, propertyInfo.GetValue (o, index: null))); } return (ExpandoObject) expando; }
erlando
6

Вместо использования dynamicтипа модели в частичном представлении.

Вы можете вызывать атрибуты анонимного объекта, используя @ViewData.Eval("foo")вместо @Model.foo.

Потом можно убрать @Model dynamicиз поля зрения.

Я недавно столкнулся с этой проблемой при передаче некоторых атрибутов между представлениями для интеграции комментариев в социальных сетях Facebook. Пример кода:

Html.RenderPartial(@"Layouts/Partials/_Comments", new {currentUrl = Model.CurrentPage.GetAbsoluteUrl(), commentCount = 5 });

Тогда, на мой взгляд, у меня был только этот div:

<div class="fb-comments" data-href="@ViewData.Eval("currentUrl")" data-numposts="@ViewData.Eval("commentCount")" data-width="100%"></div>
JamesG
источник
0

Я не уверен, что вы получаете эту ошибку, потому что вы не реализуете обходной путь. у меня такая же ошибка при частичном просмотре. решение заключалось в том, чтобы просто очистить сборку и перестроить ее. если синтаксис правильный, код должен работать, но движок бритвы может неправильно обновлять изменения кода.

Горан
источник
0

Я решил эту проблему с помощью словаря.

 @Html.Partial("_Partial", new Dictionary<string, string> { { "Key1", "Val1" }, { "Key2", "Val2" }, { "Key3", "Val3" } });
Герад Гельденхейс
источник
-6

Чтобы использовать dynamicтип, вам необходимо указать Microsoft.CSharpсборку

the_joric
источник