c# - asp.net mvc 3 razor view-> fuertemente tipeado Lista de problema de tupla
asp.net-mvc-3 tuples (6)
Esto ha pasado por algunas oleadas de discusión internamente y me temo que el producto final es que no vamos a poder arreglar esto para Razor 2.0 (MVC 4). Permítanme dar un poco de información sobre el razonamiento.
En primer lugar, es importante tener en cuenta que el analizador Razor analiza intencionalmente el C # lo menos posible. La razón principal de esto es darle la libertad al C # que desea sin que nos estorbemos. Como resultado (¡y usted mismo puede verificar esto ahora revisando el código!) No analizamos los nombres de los tipos en las directivas @inherits
y @model
, simplemente ejecutamos hasta el final de la línea. También somos el motor de análisis detrás del editor Razor, lo que significa que debemos admitir declaraciones parcialmente completas como @model Foo<Bar
que técnicamente no es válido, pero si estuvieras escribiendo @model Foo<Bar>
esto sería un paso intermedio esperado, entonces deberíamos ser capaces de manejarlo.
Ahora, debemos considerar qué pasaría si decidimos cambiar la forma en que generamos el código. Como dice la documentación de CodeTypeReference , deberíamos usar la sintaxis 1[[ ... ]]
para definir el genérico. Sin embargo, aún estaríamos insertando lo que escribiera el usuario, así que si @model Foo<Bar>
le diremos a CodeDOM que el tipo de base es algo así como System.Web.Mvc.WebViewPage`1[[Foo<Bar>]]
. Como puede ver, todavía terminamos con <>
en el nombre del tipo. Como resultado, tomamos la decisión de utilizar el hecho de que CodeDOM generalmente no se queja de la sintaxis <>
y (Of ...)
(en VB) para abrirse paso en torno a este problema.
Incluso analizar el nombre de tipo completo que proporcionó sería difícil dado que tendríamos que manejar declaraciones incompletas como @model Foo<Bar, Baz
. De hecho, también haría al editor muy frágil, ya que el editor realmente depende de que podamos decirles exactamente qué rango de mapas de Razor se correlaciona con qué rango de código generado C # / VB, y si introducimos capas de traducción adicionales (tales como la traducción CodeDOM estará haciendo si usamos []
o incluso las otras sobrecargas del constructor CodeTypeReference) ya no podemos hacer esas garantías al editor y verá un comportamiento extraño
Eso nos deja con la solución, que es simplemente evitar el uso de muchos argumentos genéricos. En realidad, existen varias razones para evitar el uso de Tuple de esta manera, ya que el uso de una clase de modelo personalizada le permitiría nombrar las propiedades involucradas, y le daría una mayor flexibilidad al agregar propiedades (con una tupla, debe actualizar el Controlador y Ver cuando quieras agregar una "propiedad" a tu tupla). Habiendo dicho eso, estamos vigilando este problema y mirando cómo podemos hacer un mejor trabajo con esto después de 4.0. Y ahora que somos de código abierto, estaremos encantados de escuchar sus sugerencias e incluso aceptar su código.
No dude en ponerse en contacto conmigo (el correo electrónico está en mi perfil SO) o continúe discutiendo esto en los comentarios si tiene alguna pregunta. ¡Solo quería darte el contexto que mereces por haber puesto tanto trabajo excelente en rastrear esto!
-Andrew Nurse (Dev en Razor parser)
Estoy teniendo un problema extraño con la vista de navaja asp.net MVC.
Quiero que mi modelo sea una List<Tuple<string, int, int, int, int>>
que es perfectamente válida en mis otros métodos c #. Pero cuando lo @model
en la declaración @model
, parece que solo selecciona la parte de la cadena de la tupla. Entonces no tengo nada. Solo elemento1.
Este problema no está presente si lo hago enlazar a una Tupla en lugar de a la Lista.
Parece que el código generado es incorrecto, ¿entonces quizás esto es un error en la vista de la maquinilla de afeitar?
El error que obtengo en la compilación es:
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS1003: Syntax error, ''>'' expected
Source Error:
Line 27:
Line 28:
Line 29: public class _Page_Views_Dashboard_DestinationStatistics_cshtml : System.Web.Mvc.WebViewPage<List<Tuple<string {
Line 30:
Line 31: #line hidden
Para aislar este problema, hice lo siguiente:
Crea un proyecto asp.net mvc vacío. Crea una nueva vista Pasado el siguiente código
@model List<Tuple<string, int, int, int, int>>
@foreach (var stat in Model)
{
<tr>
<td>
@stat.Item1
</td>
<td>
@stat.Item2
</td>
<td>
@stat.Item3
</td>
<td>
@stat.Item4
</td>
<td>
@stat.Item5
</td>
</tr>
}
Sé que puedo crear una clase o una estructura para contener los datos. Solo pregunta por curiosidad
EDITAR: Resuelto e informado al equipo de MVC aquí http://aspnet.codeplex.com/workitem/8652
Me encontré con este problema hoy. Estaba devolviendo cierta información sobre la actividad del usuario y había intentado utilizar una definición de modelo de
@model List<Tuple<string,bool,DateTime,DateTime,DateTime>>
@* Name, Online, Created, Login, Active *@
La razón es que estoy un poco cansado de hacer clases de un solo uso para viewmodels, así que es por eso que hago esto para un uso simple. Recibí el mismo error que tú. @model
eludir el error usando diferentes combinaciones de tuplas en el @model
pero fue en vano.
Lo que terminó funcionando fue simplemente usar ViewBag
. Para tener en cuenta, el model
se mantiene en el ViewBag
todos modos por lo que no hay ningún problema al usarlo en esta capacidad.
En mi método actionresult, simplemente asigné la lista de tuplas a un valor de viewbag
ViewBag.listTuple = listOfTuples;
y luego en la vista me echo de vuelta
@{
List<Tuple<string,bool,DateTime,DateTime,DateTime>> tuples = ViewBag.listTuple;
}
Y eso fue eso. Ran funcionó bien No estoy diciendo que esta sea la solución perfecta, pero es una solución de trabajo .
Me encontré con este problema porque estaba usando un Tuple
de Tuples
:
@model Tuple<Tuple<IEnumerable<int>, int, int>, Tuple<float, float, float>>
No hace falta decir que a Razor no le gustó eso. De todos modos, creo que es interesante cómo los análisis se dividen en el número total de tipos, en lugar de la cantidad de tipos en el objeto de nivel superior.
Mi solución
Terminé yendo con un modelo de vista que simples marcadores de posición creados para las tuplas internas:
public class ViewModel
{
public Tuple<IEnumerable<int>, int, int> Models { get; set; }
public Tuple<float, float, float> Selected { get; set; }
}
Estoy feliz de haber hecho el modelo de vista. Es más legible y (un poco) más fácil de manipular y razonar dentro de la vista.
Para cualquier otra persona que pueda aterrizar aquí buscando el origen de un error CS1003 en una vista Razor, el error CS1003 es un síntoma general de una falla por parte de MVC para analizar la sintaxis de Razor. Podría ser causado por muchas cosas, incluido el problema de la tupla genérica anterior.
Una cosa que he notado es que este problema ocurrirá si intentas usar un comentario de una sola línea estilo C # para anotar la declaración del modelo en una vista Razor:
BAD: @model Int32 // user ID
GOOD: @* user ID *@
@model Int32
El resaltado de sintaxis de Visual Studio no lo señalará como un problema, pero fallará en el tiempo de ejecución.
Solo quería señalar que si elimina la directiva @model, no tendrá IntelliSense que realmente se preocupa, pero la vista funciona perfectamente con cualquier número de argumentos de tupla.
EDITAR He recortado algunos de los comentarios en curso aquí, solo veo el historial para ver.
Así que puedes hacer que esto funcione con 1, 2, 3 o 4 parámetros genéricos de tupla, pero no funciona con 5. Tan pronto como utilices 5 parámetros genera código como este:
public class _Page_Views_Home_Index_cshtml :
System.Web.Mvc.WebViewPage<List<System.Tuple<string {
Quería solo averiguar si se trata de una limitación de longitud de caracteres, así que generé una clase como esta:
namespace ASP{ //same namespace that the backend code for the page is generated
public class T { }
}
Y cambió la declaración del modelo:
@model List<Tuple<T,T,T,T,T>>.
Al final (ver la historia) llegué a
@inherits System.Web.Mvc.WebViewPage<Tuple<T,T,T,T,T>>
¡El mismo problema! No es un problema con la palabra clave @model ...
Me tomó un tiempo (leer la fuente MVC3 y Razor, agregando un par de pruebas a esa solución), pero aquí hay una prueba que muestra por qué obtenemos este error:
[TestMethod]
public void TestMethod()
{
System.CodeDom.CodeTypeReferenceCollection c =
new CodeDom.CodeTypeReferenceCollection();
c.Add("Tuple<T,T,T,T>");
c.Add("Tuple<T,T,T,T,T>");
//passes
Assert.AreEqual("Tuple<T,T,T,T>", c[0].BaseType);
//fails
Assert.AreEqual("Tuple<T,T,T,T,T>", c[1].BaseType);
}
Entonces, la versión de cuatro parámetros pasa, pero no la versión de 5 parámetros.
Y adivina qué, el valor real es Tuple<T
, es decir, un nombre de tipo genérico truncado truncado exactamente de la misma manera que has observado en tu código .
Tanto el analizador Razor estándar como el analizador Razor Mvc usan el tipo CodeTypeReferenceCollection
al analizar la palabra clave @inherits
o @model
. Aquí está el código para @inherits
durante la generación de código:
protected internal virtual void VisitSpan(InheritsSpan span) {
// Set the appropriate base type
GeneratedClass.BaseTypes.Clear();
GeneratedClass.BaseTypes.Add(span.BaseClass);
if (DesignTimeMode) {
WriteHelperVariable(span.Content, InheritsHelperName);
}
}
GeneratedClass.BaseTypes
es una CodeTypeReferenceCollection
- y span.BaseClass
es una cadena. A continuación, en ILSpy, el método ofensivo debe ser el método privado CodeTypeReference.Initialize(string typeName, CodeTypeReferenceOptions options)
. No tengo tiempo suficiente ahora para descubrir por qué se rompe, pero creo que es un trabajo de desarrollador de Microsoft :) Actualización a continuación - no pude resistir. Ahora sé dónde está mal
Línea de fondo
No puede usar genéricos con más de 4 parámetros en Razor @inherits
o en declaraciones @model
(al menos en C # - no sé acerca de VB). Parece que el analizador Razor está utilizando incorrectamente el tipo de CodeTypeReference
.
Actualización final - o, tuve el bocado entre mis dientes :)
Una de las cosas que hace CodeTypeReference
es quitar la información del nombre del ensamblado de un nombre de tipo pasado con una llamada al método CodeTypeReference.RipOffAssemblyInformationFromTypeName(string typeName)
.
Y, por supuesto, si lo piensas, Tuple<T,T,T,T,T>
es como un nombre de tipo calificado para ensamblaje: con el nombre de tipo = Tuple<T
, Assembly = T
, Version = T
, Culture = T
, PublicKeyToken = T
(¡si escribes un analizador realmente MALO C #!).
Efectivamente, si pasa Tuple<T,T,T,T,T,T>
como el nombre del tipo, en realidad obtiene un Tuple<T,T>
.
Mirando más profundo en el código, está preparado para recibir un nombre de tipo neutral para el lenguaje (maneja ''['' pero nada para ''<'', por ejemplo) así que, en realidad, el equipo MVC no debería simplemente entregar el nombre de tipo C # de nuestra fuente mediante.
El equipo de MVC necesita cambiar la forma en que generan el tipo de base : podrían usar el constructor public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments)
para una nueva referencia (en lugar de simplemente confiar en .Add(span.BaseClass)
creándolo ), y analizar los parámetros genéricos en sí mismos, ya que saben que el nombre del tipo será C # / estilo VB, no el estilo neutral del idioma .Net con corchetes, etc., como parte del nombre real.