EDIT I've trimmed out some of the in-progress comments here - just view the history to see.
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 { }
}
And changed the model declaration:
@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>>
Same problem! It's not a problem with the @model keyword...
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);
}
So - the four-parameter version passes, but not the 5 parameter version.
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.
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 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
Bottom line
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.
Final Update - or, I had the bit between my teeth :)
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).
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!).
Sure enough - if you pass in Tuple<T,T,T,T,T,T> as the type name - you actually get a Tuple<T,T>.
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.
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.
public class _Page_Views_Home_Index_cshtml : System.Web.Mvc.WebViewPage<global::System.Collections.Generic.List<global::System.Tuple<string {. (I've tried using theglobal::prefix to make it work). Looks like Razor parsing of the@modeldirective bunks after the first generic parameter.CodeTypeReference- see my updated (and now trimmed) answer@model List<Tuple<string, int, int, int, int>>it's really time to use a view model class and save your fellow programmers a headache in understanding your code! Tuples are a code-smell in this context.