asp.net mvc 3 razor视图 – >强types元组列表问题

我有一个asp.net MVCrazor视图奇怪的问题。

我希望我的模型是一个List<Tuple<string, int, int, int, int>> ,这在我的其他c#方法中是完全有效的。 但是当我把它粘贴到@model声明中时,它似乎只挑出了元组的string部分。 所以我没有整数 只有item1。

这个问题是不存在的,如果我把它绑定到一个元组而不是列表。

似乎生成的代码是错误的,所以也许这是一个剃刀视图中的错误?

我编译的错误是:

 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 

为了隔离这个问题,我做了以下事情:

创build一个空的asp.net mvc项目。 创build一个新的视图。 通过以下代码。

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

我知道我可以创build一个类或一个结构来保存数据。 只是出于好奇而问

编辑:解决并向MVC团队报告http://aspnet.codeplex.com/workitem/8652

编辑我已经修剪了一些正在进行的评论在这里 – 只要查看历史就可以看到。

所以你可以使用1,2,3或者4元组generics参数来工作,但是它不能和5一起工作。只要你使用了5个参数,它就会产生这样的代码:

  public class _Page_Views_Home_Index_cshtml : System.Web.Mvc.WebViewPage<List<System.Tuple<string { 

我想知道是否是字符长度的限制,所以我产生了这样一个类:

 namespace ASP{ //same namespace that the backend code for the page is generated public class T { } } 

并更改了模型声明:

 @model List<Tuple<T,T,T,T,T>>. 

到底(看历史)我到了

 @inherits System.Web.Mvc.WebViewPage<Tuple<T,T,T,T,T>> 

同样的问题! 这不是@model关键字的问题…

花了一段时间(阅读MVC3和Razor源代码,添加了一些testing到这个解决scheme) – 但是这里是一个testing,显示了为什么我们得到这个错误:

 [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参数版本。

并猜测是什么 – 实际值是Tuple<T – 即截断的genericstypes名称截断与您在代码中观察到的方式完全相同

parsing@inherits@model关键字时,标准Razorparsing器和Mvc Razorparsing器都使用CodeTypeReferenceCollectiontypes。 这是代码生成过程中@inherits代码:

 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是一个string。 在ILSpy之后,违规方法必须是私有方法CodeTypeReference.Initialize(string typeName, CodeTypeReferenceOptions options) 。 我现在还没有足够的时间来弄清楚为什么它会中断 – 但是这是微软开发者的工作,我想:) 下面的更新 – 无法抗拒。 我现在知道哪里错了

底线

您不能在Razor @inherits@model语句中使用多于4个参数的generics(至less在C#中 – 不知道VB)。 看来Razorparsing器不正确地使用CodeTypeReferencetypes。

最终更新 – 或者,我有我的牙齿之间:)

CodeTypeReference所做的一件事是通过调用方法CodeTypeReference.RipOffAssemblyInformationFromTypeName(string typeName)从传递的types名称中去除程序集名称信息。

当然,如果你仔细想想, Tuple<T,T,T,T,T>就像一个组件限定的types名称:使用types名称= Tuple<T ,Assembly = T ,Version = T ,Culture = T ,PublicKeyToken = T (如果你写了一个真正坏的C#parsing器!)。

果然 – 如果你传入Tuple<T,T,T,T,T,T>作为types名称 – 你实际上得到了一个Tuple<T,T>

仔细研究代码,它已经准备好接受一个与语言无关的types名称(例如处理'['但没有任何'<'),所以实际上,MVC团队不应该直接从我们的源代码中直接传递C#types名通过。

MVC团队需要改变他们如何生成基types – 他们可以使用public CodeTypeReference(string typeName, params CodeTypeReference[] typeArguments)构造函数来创build一个新的引用(而不是仅仅依靠.Add(span.BaseClass)创build它),并parsinggenerics参数本身,因为他们知道types名称将是C#/ VB风格 – 不是语言中立.Net风格与括号等作为实际名称的一部分。

这已经经历了几次内部的讨论,最终的结果恐怕是我们无法修复Razor 2.0(MVC 4)。 让我来介绍一下这个推理。

首先,注意到Razorparsing器尽可能less的分析C#是非常重要的。 主要的原因是给你的C#你想要的自由,而不会阻碍我们。 因此(现在你可以通过检查代码来validation这一点),我们不会在@inherits@model指令中parsingtypes名称,我们只是运行直到行尾。 我们也是Razor编辑器背后的parsing引擎,这意味着我们必须支持部分完整的语句,比如@model Foo<Bar ,这在技术上是无效的,但是如果你input@model Foo<Bar>这将是一个预期的中间步骤,所以我们应该能够处理它。

现在,我们需要考虑我们决定改变生成代码的方式会发生什么。 正如CodeTypeReference文档所述,我们必须使用1[[ ... ]]语法来定义generics。 然而,我们仍然会插入任何用户input,所以如果你键入@model Foo<Bar>我们会告诉CodeDOM,基本types是类似于System.Web.Mvc.WebViewPage`1[[Foo<Bar>]] 。 正如你所看到的,我们仍然以types名称中的<>结尾。 因此,我们决定使用CodeDOM通常不会抱怨<>(Of ...) (在VB中)的语法来解决这个问题。

即使parsing你提供的整个types名称也是困难的,因为我们不得不处理像@model Foo<Bar, Baz这样的不完整的语句。 事实上,它也会使编辑器变得非常脆弱,因为编辑器实际上依赖于我们能够准确地告诉他们哪些范围的剃刀文本映射到C#/ VB生成的代码的范围,并且如果我们引入额外的翻译层(例如因为如果我们使用[]或CodeTypeReference构造函数的其他重载,CodeDOM将会这样做),我们不能再向编辑提供这些保证,您将看到奇怪的行为

所以这就给我们留下了一个解决方法,就是避免使用这个generics参数。 实际上有很多原因可以避免以这种方式使用Tuple,因为使用自定义模型类将允许您命名所涉及的属性,并且在添加属性时给您更大的灵活性(使用元组,必须更新Controller和查看当你想添加一个“属性”到你的元组)。 话虽如此,我们正在密切关注这个问题,看看我们如何在4.0之后做得更好。 而现在我们是开源的,我们很乐意听取您的build议,甚至接受您的代码!

请不要犹豫与我联系(电子邮件是在我的SOconfiguration文件中),或者如果您有任何疑问,请在评论中继续讨论。 我只是想给你一些你应得的背景情况,因为把这么多优秀的工作放在了这个位置上!

– 安德鲁护士(Dev on Razor parser)

我今天遇到这个问题。 我正在返回一些关于用户活动的信息,并试图使用模型定义

 @model List<Tuple<string,bool,DateTime,DateTime,DateTime>> @* Name, Online, Created, Login, Active *@ 

原因是我有些厌倦了为viewmodels制作一次性使用类,所以我这样做是为了简单的使用。 我收到了和你一样的错误。 我试图通过在@model使用不同的元组组合来绕过错误,但无济于事。

最后工作的是简单地使用ViewBag 。 要注意的是,该model无论如何都保存在ViewBag ,所以在这个function中没有任何问题使用它。

在我的actionresult方法中,我简单地将元组列表分配给viewbag值

 ViewBag.listTuple = listOfTuples; 

然后在我看来,我把我投回

 @{ List<Tuple<string,bool,DateTime,DateTime,DateTime>> tuples = ViewBag.listTuple; } 

就是这样。 蛮好的 我并不是说这是一个完美的解决scheme,但它是一个可行的解决方法

对于任何可能在这里寻找Razor视图中的CS1003错误来源的人,CS1003错误是MVCparsingRazor语法失败的一般症状。 这可能是由许多事情引起的,包括上面的通用元组问题。

我注意到的一件事是,如果您尝试使用C#样式的单行注释来在Razor视图中注释模型声明,则会发生此问题:

 BAD: @model Int32 // user ID GOOD: @* user ID *@ @model Int32 

Visual Studio语法突出显示不会将其标记为问题,但在运行时会失败。

只是想指出,如果你删除@model指令,你将不会有真正关心的intellisense,但是这个视图可以和任何数量的元组参数一起工作。

我遇到了这个问题,因为我使用了Tuple Tuples

 @model Tuple<Tuple<IEnumerable<int>, int, int>, Tuple<float, float, float>> 

不用说,剃刀不喜欢那个。 无论如何,我认为有趣的是,parsing如何在types总数上打破,而不是顶级对象types的数量。

我的解决方法

我结束了与视图模型简单地创build了内部元组的占位符:

 public class ViewModel { public Tuple<IEnumerable<int>, int, int> Models { get; set; } public Tuple<float, float, float> Selected { get; set; } } 

我很高兴我制作了视图模型。 它更易于阅读,并且(稍微)更容易在视图内部进行操作和推理。