问题描述
我在使用 asp.net MVC 剃刀视图时遇到了一个奇怪的问题.
I'm having an odd problem with asp.net MVC razor view.
我希望我的模型是一个 List<tuple></tuple
这在我的其他 c# 方法中完全有效.但是当我将它粘贴到 @model
声明中时,它似乎只挑选出元组的字符串部分.所以我没有整数.只有 item1.
I want my model to be a List<Tuple<string, int, int, int, int>>
which is perfectly valid in my other c# methods. But when I paste it into the @model
declaration it seems to only pick out the string part of the tuple. So I have no ints. Only item1.
如果我将它绑定到元组而不是列表,则不会出现此问题.
This problem is not present if I make it bind to a Tuple instead of the List.
好像生成的代码有问题,所以这可能是剃刀视图中的一个错误?
Seems like the generated code is wrong, so perhaps this is a bug in razor view?
我在编译时遇到的错误是:
The error I get at compilation is:
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
为了隔离这个问题,我做了以下事情:
To isolate this problem I did the following thing:
创建一个空的 asp.net mvc 项目.创建一个新视图.粘贴以下代码.
Create an empty asp.net mvc project. Create a new view. Past the following code.
@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>
}
我知道我可以创建一个类或一个结构来保存数据.只是出于好奇而问
I know I can just create a class or a struct to hold the data instead. Just asking out of curiosity
在此处解决并报告给 MVC 团队 http://aspnet.codeplex.com/workitem/8652
Solved and reported to the MVC team here http://aspnet.codeplex.com/workitem/8652
推荐答案
EDIT 我在这里删减了一些正在进行的评论 - 只需查看历史记录即可.
因此,您可以使用 1、2、3 或 4 元组通用参数进行此操作,但不适用于 5.一旦您使用 5 个参数,它就会生成如下代码:
So you can make this work with 1, 2, 3 or 4 tuple generic parameters but it doesn't work with 5. As soon as you use 5 parameters it generates code like this:
public class _Page_Views_Home_Index_cshtml :
System.Web.Mvc.WebViewPage<List<System.Tuple<string {
我只想知道它是否是字符长度限制,所以我生成了一个这样的类:
I wanted to just find out if it's a character-length limitation, so I generated a class like this:
namespace ASP{ //same namespace that the backend code for the page is generated
public class T { }
}
并更改了模型声明:
@model List<Tuple<T,T,T,T,T>>.
最后(见历史)我得到了
In the end (see the history) I got to
@inherits System.Web.Mvc.WebViewPage<Tuple<T,T,T,T,T>>
同样的问题!@model关键字没有问题...
Same problem! It's not a problem with the @model keyword...
花了一些时间(通读 MVC3 和 Razor 源代码,向该解决方案添加了一些测试) - 但这里有一个测试说明了我们为什么会收到此错误:
It took a while (reading through the MVC3 and Razor source, adding a couple of tests to that solution) - but here's a test that shows the why we get this 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);
}
所以 - 四参数版本通过,但没有 5 参数版本.
So - the four-parameter version passes, but not the 5 parameter version.
猜猜看 - 实际值是 Tuple<t
- 即截断的泛型类型名称 截断方式与您在代码中观察到的完全相同.</t
And guess what- the actual value is Tuple<T
- i.e. a truncated generic type name truncated exactly the same way that you've observed in your code.
标准 Razor 解析器和 Mvc Razor 解析器在解析 @inherits
或 @model
关键字时都使用 CodeTypeReferenceCollection
类型.下面是代码生成过程中 @inherits
的代码:
Both the standard Razor parser and the Mvc Razor parser use the CodeTypeReferenceCollection
type when parsing either the @inherits
or @model
keyword. Here's the code for @inherits
during code generation:
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
是一个 CodeTypeReferenceCollection
- 而 span.BaseClass
是一个字符串.在 ILSpy 中,有问题的方法必须是私有方法 CodeTypeReference.Initialize(string typeName, CodeTypeReferenceOptions options)
.我现在没有足够的时间来弄清楚它为什么会中断 - 但我认为这是微软开发人员的工作:) 下面更新 - 无法抗拒.我现在知道哪里不对了
GeneratedClass.BaseTypes
is a CodeTypeReferenceCollection
- and span.BaseClass
is a string. Following that through in ILSpy, the offending method must be the private method CodeTypeReference.Initialize(string typeName, CodeTypeReferenceOptions options)
. I've not enough time now to figure out why it breaks - but then that's a Microsoft developer's job I think :) Update below - couldn't resist. I now know where it's wrong
您不能在 Razor @inherits
或 @model
语句中使用超过 4 个参数的泛型(至少在 C# 中 - 不了解 VB).Razor 解析器似乎错误地使用了 CodeTypeReference
类型.
You can't use generics with more than 4 parameters in either Razor @inherits
or @model
statements (at least in C# - don't know about VB). It appears that the Razor parser is incorrectly using the CodeTypeReference
type.
CodeTypeReference
所做的一件事是通过调用方法 CodeTypeReference.RipOffAssemblyInformationFromTypeName(string typeName)
从传递的类型名称中剥离程序集名称信息.
One of the things that CodeTypeReference
does is strip off assembly name information from a passed type name with a call to the method CodeTypeReference.RipOffAssemblyInformationFromTypeName(string typeName)
.
当然,如果你仔细想想 - Tuple
就像一个程序集限定的类型名称:类型名称 = 元组<t、Assembly = T
、Version=T
、Culture=T
、PublicKeyToken=T
>(如果您编写了一个非常糟糕的 C# 解析器!).</t
And of course, if you think about it - Tuple<T,T,T,T,T>
is just like an assembly-qualified type name: With the type name = Tuple<T
, Assembly = T
, Version=T
, Culture=T
, PublicKeyToken=T
(if you write a really BAD C# parser!).
果然 - 如果你传入 Tuple
作为类型名称 - 你实际上得到了一个 Tuple.
Sure enough - if you pass in Tuple<T,T,T,T,T,T>
as the type name - you actually get a Tuple<T,T>
.
深入研究代码,它准备接收一个语言中立的类型名(例如,处理 '[' 但没有处理 '<'),因此,实际上,MVC 团队不应该只处理 C# 类型名直接从我们的源头开始.
Looking deeper into the code, it's primed to receive a language-neutral typename (handles '[' but nothing for '<', for example) so, actually, the MVC team shouldn't just be handing the C# typename from our source straight through.
MVC 团队需要改变他们生成基本类型的方式 - 他们可以使用 public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments)
新引用的构造函数(而不是仅仅依靠 .Add(span.BaseClass)
创建它),并解析泛型参数本身,因为它们知道类型名称将是 C#/VB 风格 - 不是语言中立的 .Net 风格,带有括号等作为实际名称的一部分.
The MVC team needs to change how they generate the base type - They could use the public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments)
constructor for a new reference (instead of just relying on the .Add(span.BaseClass)
creating it), and parse the generic parameters themselves since they know that the type name will be C#/VB style - not language-neutral .Net style with brackets etc as part of the actual name.
这篇关于asp.net mvc 3 剃刀视图 ->元组问题的强类型列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,WP2