dynamic - tabla - El tipo dinámico anónimo en Razor causa la excepción RuntimeBinderException
mostrar datos de una tabla en mvc (10)
Ahora en sabor recursivo
public static ExpandoObject ToExpando(this object obj)
{
IDictionary<string, object> 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;
}
Me aparece el siguiente error:
''objeto'' no contiene una definición para ''Nombre de clasificación''
Cuando observa el tipo dinámico anónimo, claramente tiene RatingName.
Me doy cuenta de que puedo hacer esto con un Tuple, pero me gustaría entender por qué aparece el mensaje de error.
Basado en la respuesta aceptada, he anulado en el controlador para que funcione en general y detrás de escena.
Aquí está el código:
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<string, object> 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");
}
}
}
Ahora puede pasar un objeto anónimo como modelo y funcionará como se espera.
El uso del método ToExpando es la mejor solución.
Aquí está la versión que no requiere el 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 de un tipo anónimo y luego tratar de convertir el objeto anónimo a un ExpandoObject
como este ...
var model = new
{
Profile = profile,
Foo = foo
};
return View(model.ToExpando()); // not a framework method (see other answers)
Puedes simplemente crear el ExpandoObject
directamente:
dynamic model = new ExpandoObject();
model.Profile = profile;
model.Foo = foo;
return View(model);
Luego, en su vista, establece el tipo de modelo como dynamic @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.
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 las vistas de MVC y acceder a ellas usando la dinámica
La razón para esto es que el tipo anónimo que se pasa en el controlador en interno, por lo que solo se puede acceder desde el ensamblaje en el que se ha declarado. Dado que las vistas se compilan por separado, el enlazador dinámico se queja de que no puede pasar ese límite de ensamblaje.
Pero si lo piensas, 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, la carpeta dinámica predeterminada se está esforzando para aplicar las reglas de compilación de C # (donde no puede acceder a los miembros internos), en lugar de permitirle hacer lo que permite el tiempo de ejecución de CLR.
Escribió una aplicación de consola y agrega Mono.Cecil como referencia (ahora puede agregarlo desde NuGet ) y luego escribe el fragmento de código:
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
});
}
El código anterior obtendría el archivo de ensamblaje de args de entrada y usaría Mono.Cecil para cambiar la accesibilidad de interno a público, y eso resolvería el problema.
Podemos ejecutar el programa en el evento Post Build del sitio web. Escribí una publicación de blog sobre esto en chino, pero creo que puedes leer el código y las instantáneas. :)
La razón de RuntimeBinderException activada, creo que hay una buena respuesta en otras publicaciones. Solo me enfoco en explicar cómo realmente lo hago funcionar.
Consulte referir a @DotNetWise y vistas de enlace con la colección de tipos anónimos en ASP.NET MVC ,
En primer lugar, crea una clase estática para la extensión
public static class impFunctions
{
//converting the anonymous object into an ExpandoObject
public static ExpandoObject ToExpando(this object anonymousObject)
{
//IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(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;
}
}
En el controlador
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);
}
En View, @model IEnumerable (dinámico, no una clase de modelo), esto es muy importante, ya que vamos a vincular el objeto de tipo anónimo.
@model IEnumerable<dynamic>
@*@foreach (dynamic item in Model)*@
@foreach (var item in Model)
{
<div>[email protected], [email protected]</div>
}
El tipo en foreach, no tengo ningún error ya sea usando var o dynamic .
Por cierto, crear un nuevo ViewModel que coincida con los nuevos campos también puede ser la forma de pasar el resultado a la vista.
Los tipos anónimos que tienen propiedades internas son, en mi opinión, una decisión de diseño de .NET pobre.
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>
}
Puede usar la interfaz impromptu del marco para envolver un tipo anónimo en una interfaz.
Simplemente devolverá un IEnumerable<IMadeUpInterface>
y al final de su uso de Linq .AllActLike<IMadeUpInterface>();
esto funciona porque llama a la propiedad anónima utilizando el DLR con un contexto del ensamblado que declaró el tipo anónimo.
Voy a robar un poco de https://.com/a/7478600/37055
Si instala el paquete dynamitey , puede hacer esto:
return View(Build<ExpandoObject>.NewObject(RatingName: name, Comment: comment));
Y los campesinos se regocijan.