Modelo dinámico MVC Razor, 'objeto' no contiene definición para 'PropertyName'

106

Usando MVC 3 con el motor de vista de Razor. Tengo esta vista:

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

Se llama desde otra vista usando este código:

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

En ambas vistas, cuando las depuro y veo Model, parece contener el objeto correcto. Cuando ejecuto el código, aparece un error en la línea "var products =" que dice:

'objeto' no contiene una definición de 'Productos'

¿Alguien puede explicarme por qué recibo ese error? Nuevamente, cuando miro el objeto Modelo en modo de depuración, se ve bien (tiene 2 propiedades: Productos y Umbral)

Ruud van Falier
fuente

Respuestas:

150

¿Está pasando una instancia de una clase anónima como modelo de vista? Acabo de probar esto (modelo de vista dinámica en CSHTML) y obtuve el mismo error que el suyo al usar una clase anónima, pero funcionó bien si creé una clase con nombre. Busqué pero no he visto esto documentado en ninguna parte.

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

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

EDITAR # 1:

Según David Ebbo , no se puede pasar un tipo anónimo a una vista de tipo dinámico porque los tipos anónimos se compilan como internal. Dado que la vista CSHTML se compila en un ensamblado separado, no puede acceder a las propiedades del tipo anónimo.

EDITAR # 2:

David Ebbo ha editado su publicación con esta aclaración:

Nota (22/12/2011): ahora que MVC 3 tiene soporte directo para dinámica, la técnica siguiente ya no es necesaria. ¡Esta publicación es de hecho lo que llevó a integrar la función en MVC!

Lucas
fuente
1
Es bueno saber la edición. Simplemente tuve el mismo problema y no entendí la WTF allí. Gracias por la explicación.
Yanick Rochon
18
EDIT # 2 sugiere que ahora (MVC> 3) es posible hacer una línea marcada con "error"? return View(new { Foo = 1, Bar = "test" });? Porque estoy usando MVC 4 y sigo obteniendo "el objeto no contiene una definición para Foo"
deportes
@sports me too ... ¿has encontrado una solución? (al lado del ToExpandouno)
Alex
2
Entonces, ahora en 2018 usando ASP.NET Core 2.1 y vistas Razor, encuentro que el error en la pregunta original todavía me está molestando. Así que no sé de qué se trata esta charla sobre MVC 3 arreglando esto, ya que todavía parece roto.
Andrew Arnott
41

En .NET 4.0, los tipos anónimos se pueden convertir fácilmente en ExpandoObjects y, por lo tanto, todos los problemas se solucionan con la sobrecarga de la conversión en sí. Mira aquí

Adaptabi
fuente
De nada. Tal vez esto patea M $ para que los tipos anónimos sean más utilizables
Adaptabi
Sin embargo, ¿esto se aplica a los parciales? Recibí un error de que los parciales no se pueden enviar dinámicamente ...
John Bubriski
1
¿Qué parciales? ¿Puede dar un ejemplo?
Adaptabi
27

Esto no tiene nada que ver con los tipos anónimos que tienen propiedades internas.

Es perfectamente posible pasar tipos anónimos de una vista a una vista parcial

Hoy encontré el mismo problema y no tenía nada que ver (directamente) con el problema de pasar tipos anónimos y sus internalpropiedades inherentes .

Como tal, en relación con la pregunta de los PO, la respuesta de @Lucas es irrelevante, aunque la solución funcionará .

En la pregunta de OP, se pasa un tipo anónimo de una vista en el ensamblaje X a un parcial en el ensamblaje X , por lo tanto, el problema que David Ebbo describió de que las propiedades son internas para los tipos anónimos no tiene importancia; los tipos compilados para la vista, el tipo parcial y el anónimo están todos contenidos en el mismo ensamblado .

Entonces, ¿qué está causando la falla repentina de pasar un tipo anónimo de una vista a una parcial?

Al menos en mi situación, descubrí que se debía a tener otra vista en la MISMA CARPETA que especifica un tipo de modelo que no se puede resolver . Las vistas se compilan en tiempo de ejecución, por lo que tendría sentido, ya que una falla en el tiempo de ejecución para compilar las vistas también significaría una falla al compilar los tipos dinámicos y el parcial simplemente recibiría un object. No es inmediatamente obvio lo que está sucediendo, pero en el ejemplo específico de OP (y en el mío), esta es más que probable la causa del problema.

Es interesante notar que si el tipo de modelo es correcto pero otra parte de la vista no se compila, los tipos anónimos no se ven afectados de la misma manera. Esto debe depender de cómo Razor divide la compilación dinámica de los componentes de la vista.

Una vez que corrija la vista ofensiva, reconstruya la solución completa o limpie y reconstruya el proyecto antes de verificar si está arreglado.

Para asegurarse de que esto no lo atrape nuevamente, puede habilitar la compilación en tiempo de compilación de sus vistas de Razor agregando esto a su csprojarchivo:

<PropertyGroup>
    <MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
Joshcomley
fuente
2
Esto solucionó mi problema: usar "@model dynamic" inicialmente parecía la solución correcta, pero de hecho me estaba llevando por el camino equivocado.
Crimbo
Limpié la solución, la reconstruí y el error desapareció ... 121 votos positivos perdidos.
maxbeaudoin
Actualicé mi respuesta para reflejar el soporte de MVC para modelos de vista dinámica desde MVC 3.
Lucas
Habilitar la compilación de vistas de vez en cuando siempre es útil para una gran base de código. Revela todo tipo de problemas, errores tipográficos, errores con T4MVC gracias a la escritura fuerte que introdujo, etc.
Denis The Menace
Ah, claro: acabo de notar que estamos hablando de pasar de una vista a una parcial aquí. No de un controlador a una vista, que es mi problema.
mwardm
9

Agregue la siguiente clase en cualquier lugar de su solución (use el espacio de nombres del sistema, para que esté listo para usar sin tener que agregar ninguna referencia):

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

        }
    }

Cuando envíe el modelo a la vista, conviértalo a Expando:

    return View(new {x=4, y=6}.ToExpando());
Segev -CJ- Shmueli
fuente
1
Me parece una sobrecarga innecesaria para mí crear un objeto dinámico primero y luego crear un ExpandoObject ... Simplemente cree el ExpandoObject en su lugar ..
Baz1nga
@ Baz1nga No puedes hacer ... new Expando () {prop = value, ...}, lo que lo hace problemático. Estoy usando JObject de Json.Net para un uso similar.
Tracker1
3
Se siente mal tener HtmlHelper allí ... public static ExpandoObject ToExpando (este objeto o) {IDictionary <string, object> expando = new ExpandoObject (); foreach (var propertyInfo en o.GetType (). GetProperties ()) {expando.Add (new KeyValuePair <string, object> (propertyInfo.Name, propertyInfo.GetValue (o, index: null))); } return (ExpandoObject) expando; }
erlando
6

En lugar de utilizar el dynamictipo de modelo dentro de la vista parcial.

Puede llamar a los atributos del objeto anónimo utilizando en @ViewData.Eval("foo")lugar de @Model.foo.

Entonces puedes eliminarlo @Model dynamicde la vista.

Me encontré con este problema recientemente al pasar algunos atributos entre vistas para la integración de comentarios sociales de Facebook. Código de ejemplo:

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

Entonces, en mi opinión, acabo de tener este div:

<div class="fb-comments" data-href="@ViewData.Eval("currentUrl")" data-numposts="@ViewData.Eval("commentCount")" data-width="100%"></div>
JamesG
fuente
0

No estoy seguro de que reciba este error porque no está implementando la solución alternativa. obtuve el mismo error en una vista parcial. la solución fue simplemente limpiar la construcción y reconstruirla. si la sintaxis es correcta, el código debería funcionar, pero es posible que el motor de la maquinilla de afeitar no esté actualizando los cambios de código correctamente.

goran
fuente
0

Resolví este problema usando un diccionario.

 @Html.Partial("_Partial", new Dictionary<string, string> { { "Key1", "Val1" }, { "Key2", "Val2" }, { "Key3", "Val3" } });
Gerade Geldenhuys
fuente
-6

Para usar el dynamictipo, necesita hacer referencia al Microsoft.CSharpensamblaje

the_joric
fuente