El tipo anónimo dinámico en Razor provoca RuntimeBinderException
Recibo el siguiente error:
'objeto' no contiene una definición para 'RatingName'
Cuando observa el tipo dinámico anónimo, claramente tiene RatingName.
Me doy cuenta de que puedo hacer esto con una tupla, pero me gustaría entender por qué aparece el mensaje de error.
En mi opinión, los tipos anónimos que tienen propiedades internas son una mala decisión de diseño del marco .NET.
Aquí hay una extensión rápida y agradable para solucionar este problema, es decir, convirtiendo el objeto anónimo en un ExpandoObject de inmediato.
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
Es muy fácil de usar:
return View("ViewName", someLinq.Select(new { x=1, y=2}.ToExpando());
Por supuesto en su opinión:
@foreach (var item in Model) {
<div>x = @item.x, y = @item.y</div>
}
Encontré la respuesta en una pregunta relacionada . La respuesta se especifica en la publicación del blog de David Ebbo. Pasar objetos anónimos a vistas MVC y acceder a ellos usando dinámica
La razón de esto es que el tipo anónimo se pasa en el controlador de forma interna, por lo que solo se puede acceder a él desde el ensamblado en el que se declara. Dado que las vistas se compilan por separado, el enlazador dinámico se queja de que no puede superar ese límite de ensamblaje.
Pero si lo piensas bien, esta restricción de la carpeta dinámica es en realidad bastante artificial, porque si usas la reflexión privada, nada te impide acceder a esos miembros internos (sí, incluso funciona en confianza media). Por lo tanto, el enlazador dinámico predeterminado hace todo lo posible para hacer cumplir las reglas de compilación de C# (donde no se puede acceder a los miembros internos), en lugar de permitirle hacer lo que permite el tiempo de ejecución de CLR.
Usar el método ToExpando es la mejor solución.
Aquí está la versión que no requiere ensamblado System.Web:
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(anonymousObject))
{
var obj = propertyDescriptor.GetValue(anonymousObject);
expando.Add(propertyDescriptor.Name, obj);
}
return (ExpandoObject)expando;
}
En lugar de crear un modelo a partir de un tipo anónimo y luego intentar convertir el objeto anónimo en uno ExpandoObject
como este...
var model = new
{
Profile = profile,
Foo = foo
};
return View(model.ToExpando()); // not a framework method (see other answers)
Puedes crear ExpandoObject
directamente:
dynamic model = new ExpandoObject();
model.Profile = profile;
model.Foo = foo;
return View(model);
Luego, en su vista, configura el tipo de modelo como dinámico @model dynamic
y puede acceder a las propiedades directamente:
@Model.Profile.Name
@Model.Foo
Normalmente recomendaría modelos de vista fuertemente tipados para la mayoría de las vistas, pero a veces esta flexibilidad es útil.
Puede utilizar la interfaz improvisada del marco para incluir un tipo anónimo en una interfaz.
Simplemente devolvería un IEnumerable<IMadeUpInterface>
y al final de su uso de Linq, .AllActLike<IMadeUpInterface>();
esto funciona porque llama a la propiedad anónima usando el DLR con un contexto del ensamblado que declaró el tipo anónimo.