MVC: Strongly Typed ViewData Without A Code-Behind
// January 7th, 2008 // MVC
If you’ve tried to use strongly-typed ViewData with a ViewPage or ViewContentPage without using a code-behind file you may have run into the curious scenario of how to specify a generic in the <%@ Page %> element’s Inherits attribute.
The Problem
Let’s say you wanted to specify that the ViewData for this view would be an integer. Normally you would specify this in the code-behind like so:
1: using System;
2: using System.Web;
3: using System.Web.Mvc;
4:
5: namespace MVC_Example.Views.Home
6: {
7: public partial class Index : ViewPage<int>
8: {
9: }
10: }
Since we don’t have a code-behind file though, we have to change the .aspx file’s <%@ Page %> element. Trying to set it to the following will not work:
1: <%@ Page Language="C#" Inherits="MVC_Example.Views.Home.Index<int>" %>
The Basic Answer
The value of the Inherits attribute appears to be delivered straight to the CLR without any language specific parsing, which means we have to specify it the way the CLR wants to interpret it. I browsed around and found (here and here) some information that points out the proper way to do this:
1: <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage`1[ [System.Int32,mscorlib] ]" %>
WTF?
Eww, huh? The syntax is gnarly, but it gets the job done. Let’s break the syntax down to make sure you understand what is going on:
ParentType`1[ [ChildType,ChildTypeAssembly] ]
- “ParentType” is fairly obvious. This is the generic object that you want to create a reference to.
- “`1[ [" is a bit more obscure. The "`1" specifies the number of generics in this argument, which we'll come back to in a second. The double square brackets are required. A single square bracket will NOT work.
- "ChildType" is the type of object you wish to have the generic consume (what is the proper terminology here? anyone know?).
- "ChildTypeAssembly" is the name of the assembly (NOT the namespace) that contains the ChildType. All of your common value types will be located in "mscorlib".
Multiple Generic Types
Let's say ViewPage took two arguments as part of its generic declaration area (ie: ViewPage<X,Y>). It doesn't, by the way, but we'll pretend it does for the sake of argument; you may find this information useful someday.
To have two type parameters, you'll have to change the "`1" to a "`2" and add an extra "[ChildType, ChildTypeAssembly]“ reference, separated by a space. More parameters would continue to follow the same logic.
If you wanted to achieve the equivalent of ViewPage<int,string> it would look like this:
1: System.Web.Mvc.ViewPage`2[ [System.Int32,mscorlib], [System.String,mscorlib] ]
A Generic of Generics
A more likely scenario is that you may want to have your ViewData be a generic List<T>. In this case you’ll just nest the declarations. If you wanted to pass ViewData of the type List<string> to the ViewPage it would look like:
1: System.Web.Mvc.ViewPage`1[[System.Collections.Generic.List`1[[System.String,mscorlib]], mscorlib]]




Thanks for this, it’ll help while I port my website to the ASP.NET framework.
Keep up the good work.
Extremely impressive.
I was trying to do the same things with Pages some years ago, but I forgot to set the assembly name for the parameter type.
So I was really sure it is impossible.
how about if you need to refer to open generic type? for eg. ViewPage?
I’m not sure what you mean by an open generic type, Kihsa. As far as I know you must specify the type of the generic so that it can compile correctly. Can you give me an example of what you are trying to accomplish?
if you need to refer to MyNamespace.ClassType it would be MyNamespace.ClassType’1. So i was wondering how i could refer to MyNamespace.ClassType ? I want to be able to inject DI in generic way.
I am running the latest ASP.NET MVC RC2 and your original version appears now to work using ViewPage.
Do you have any idea at what stage support for this was added?
@Edward: That feature was added in the first Release Candidate (I think – possibly Beta). I for one am extremely excited that they found a way to fix the issue.