Динамический анонимный тип в Razor вызывает RuntimeBinderException

Я получаю следующую ошибку:

‘object’ не содержит определения для ‘RatingName’

Когда вы смотрите на анонимный динамический тип, у него явно есть RatingName.

Снимок экрана с ошибкой

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

На мой взгляд, анонимные типы, имеющие внутренние свойства, представляют собой плохое решение .NET Framework.

Вот быстрое и приятное расширение, чтобы исправить эту проблему, например, сразу же превратив анонимный объект в ExpandoObject.

public static ExpandoObject ToExpando(this object anonymousObject) { IDictionary anonymousDictionary = new RouteValueDictionary(anonymousObject); IDictionary expando = new ExpandoObject(); foreach (var item in anonymousDictionary) expando.Add(item); return (ExpandoObject)expando; } 

Он очень прост в использовании:

 return View("ViewName", someLinq.Select(new { x=1, y=2}.ToExpando()); 

Конечно, на ваш взгляд:

 @foreach (var item in Model) { 
x = @item.x, y = @item.y
}

Я нашел ответ в соответствующем вопросе . Ответ указан в сообщении блога Дэвида Эббо. Передача анонимных объектов в представления MVC и их доступ с помощью динамического

Причина этого в том, что анонимный тип передается controllerу во внутреннем, поэтому его можно получить только из сборки, в которой он был объявлен. Поскольку представления компилируются отдельно, динамическое связующее лицо жалуется, что оно не может пройти через границу сборки.

Но если вы думаете об этом, это ограничение от динамического связующего на самом деле довольно искусственно, потому что если вы используете частное reflection, ничто не мешает вам получить доступ к этим внутренним членам (да, это даже работает в средстве доверия). Таким образом, динамическое связующее по умолчанию выходит за его пределы, чтобы обеспечить соблюдение правил компиляции C # (где вы не можете получить доступ к внутренним членам) вместо того, чтобы позволить вам делать то, что позволяет среда CLR.

Использование метода ToExpando – лучшее решение.

Вот версия, которая не требует сборки System.Web :

 public static ExpandoObject ToExpando(this object anonymousObject) { IDictionary expando = new ExpandoObject(); foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(anonymousObject)) { var obj = propertyDescriptor.GetValue(anonymousObject); expando.Add(propertyDescriptor.Name, obj); } return (ExpandoObject)expando; } 

Вместо того, чтобы создавать модель из анонимного типа, а затем пытаться преобразовать анонимный объект в объект ExpandoObject как это …

 var model = new { Profile = profile, Foo = foo }; return View(model.ToExpando()); // not a framework method (see other answers) 

Вы можете просто создать ExpandoObject напрямую:

 dynamic model = new ExpandoObject(); model.Profile = profile; model.Foo = foo; return View(model); 

Затем, на ваш взгляд, вы устанавливаете тип модели динамическим динамиком @model dynamic и вы можете напрямую обращаться к свойствам:

 @Model.Profile.Name @Model.Foo 

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

Вы можете использовать интерфейс импровизированного фреймворка для переноса анонимного типа в интерфейс.

Вы просто IEnumerable и в конце использования Linq .AllActLike(); это работает, потому что оно вызывает анонимное свойство, используя DLR с контекстом сборки, объявившего анонимный тип.

Написал консольное приложение и добавил Mono.Cecil в качестве ссылки (теперь вы можете добавить его из NuGet ), а затем напишите fragment кода:

 static void Main(string[] args) { var asmFile = args[0]; Console.WriteLine("Making anonymous types public for '{0}'.", asmFile); var asmDef = AssemblyDefinition.ReadAssembly(asmFile, new ReaderParameters { ReadSymbols = true }); var anonymousTypes = asmDef.Modules .SelectMany(m => m.Types) .Where(t => t.Name.Contains("<>f__AnonymousType")); foreach (var type in anonymousTypes) { type.IsPublic = true; } asmDef.Write(asmFile, new WriterParameters { WriteSymbols = true }); } 

В приведенном выше коде был бы получен файл сборки из входных аргументов и использование Mono.Cecil для изменения доступности изнутри в общедоступное, и это решило бы проблему.

Мы можем запустить программу в событии Post Build на веб-сайте. Я написал сообщение в блоге об этом на китайском, но я считаю, что вы можете просто прочитать код и моментальные снимки. 🙂

Основываясь на принятом ответе, я переопредел в controllerе, чтобы он работал вообще и за кулисами.

Вот код:

 protected override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); //This is needed to allow the anonymous type as they are intenal to the assembly, while razor compiles .cshtml files into a seperate assembly if (ViewData != null && ViewData.Model != null && ViewData.Model.GetType().IsNotPublic) { try { IDictionary expando = new ExpandoObject(); (new RouteValueDictionary(ViewData.Model)).ToList().ForEach(item => expando.Add(item)); ViewData.Model = expando; } catch { throw new Exception("The model provided is not 'public' and therefore not avaialable to the view, and there was no way of handing it over"); } } } 

Теперь вы можете просто передать анонимный объект в качестве модели, и он будет работать, как ожидалось.

Я собираюсь немного поработать с https://stackoverflow.com/a/7478600/37055

Если вы устанавливаете динамический пакет, вы можете сделать это:

 return View(Build.NewObject(RatingName: name, Comment: comment)); 

И крестьяне радуются.

Причина запуска RuntimeBinderException, я думаю, что есть хорошие ответы в других сообщениях. Я просто сосредоточусь, чтобы объяснить, как я на самом деле заставляю его работать.

Обратившись к ответам @DotNetWise и Binding views с коллекцией анонимного типа в ASP.NET MVC ,

Во-первых, создайте статический class для расширения

 public static class impFunctions { //converting the anonymous object into an ExpandoObject public static ExpandoObject ToExpando(this object anonymousObject) { //IDictionary anonymousDictionary = new RouteValueDictionary(anonymousObject); IDictionary anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousObject); IDictionary expando = new ExpandoObject(); foreach (var item in anonymousDictionary) expando.Add(item); return (ExpandoObject)expando; } } 

В controllerе

  public ActionResult VisitCount() { dynamic Visitor = db.Visitors .GroupBy(p => p.NRIC) .Select(g => new { nric = g.Key, count = g.Count()}) .OrderByDescending(g => g.count) .AsEnumerable() //important to convert to Enumerable .Select(c => c.ToExpando()); //convert to ExpandoObject return View(Visitor); } 

В представлении, @model IEnumerable (динамический, а не модельный class), это очень важно, поскольку мы собираемся связать объект анонимного типа.

 @model IEnumerable @*@foreach (dynamic item in Model)*@ @foreach (var item in Model) {  } 

Тип foreach, у меня нет ошибки, либо с использованием var или dynamic .

Кстати, создать новый ViewModel, который соответствует новым полям, также может быть способом передачи результата в представление.

Теперь в рекурсивном вкусе

 public static ExpandoObject ToExpando(this object obj) { IDictionary expandoObject = new ExpandoObject(); new RouteValueDictionary(obj).ForEach(o => expandoObject.Add(o.Key, o.Value == null || new[] { typeof (Enum), typeof (String), typeof (Char), typeof (Guid), typeof (Boolean), typeof (Byte), typeof (Int16), typeof (Int32), typeof (Int64), typeof (Single), typeof (Double), typeof (Decimal), typeof (SByte), typeof (UInt16), typeof (UInt32), typeof (UInt64), typeof (DateTime), typeof (DateTimeOffset), typeof (TimeSpan), }.Any(oo => oo.IsInstanceOfType(o.Value)) ? o.Value : o.Value.ToExpando())); return (ExpandoObject) expandoObject; } 
  • Как использовать ResolveComponentFactory (), но со строкой в ​​качестве ключа
  • Создание анонимного типа динамически?
  • Создание динамической таблицы для добавления новой записи с помощью кнопки
  • Изменение размера массива с помощью C
  • Удаление динамически созданных элементов управления в C #
  • Давайте будем гением компьютера.