Click here to Skip to main content
Click here to Skip to main content

Support for Generics in ASP.NET Server Controls

By , 29 Jan 2009
 

Generics in ASP .NET? Why?

Anyone who has ever built a WebForms based application has no doubt come across the well-worn Repeater control. A staple of web programming, it allows for the display of a collection of items, and exposes the items through the Eval or Bind methods.

Under the hood, Eval and Bind work through Reflection in order to provide late binding support. This is also what you see if you reference properties such as:

<%# Container.DataItem.PropertyNameHere %>

The reason for this is that ASP.NET allows transparent late binding, so even though it believes that Container.DataItem is typed as 'Object', the evaluation will only occur at runtime. This leads to all sorts of problems such as:

  • Pages breaking when a property is changed, and no warnings at compile-time.
  • Reduced performance, as every data-field display point becomes a Reflection call.

If we could tell ASP.NET what the type of the data items actually are, though, then during site start-up/compilation, we would be able to ensure the right types are being used, and no late-binding ever occurs. Compile time checking of all pages, master-pages, and user control binding tags sounds good?

If that's not enough for you, what about full intellisense support?

StrongTypingIntellisense.png

Generics in ASP.NET? How?

During start-up and the first hit to a page in a given folder, ASP.NET performs batch compilation of your ASPX files. That is, the conversion of them and the content therein into actual .NET code that will be run. The trouble previously with ASP.NET Generics was that there is no syntax support in ASP.NET to specify a generic type, for example:

<asp:Repeater ID="Repeater1" runat="Server" />

is all well and good, but you can't say:

<asp:GenericRepeater<String> ID="StringRepeater" />

As a result, we're going to have to find a workaround. I've ended up taking a very similar route to another tutorial that gives a typed Repeater implementation, by Andrey Shchekin. You can find his tutorial here. The way this works is as follows:

  • We intercept compile-time construction of pages using a ControlBuilder sub-class assigned to a non-generic class.
  • This ControlBuilder's Init method switches the non-generic type for a real generic type, using type-names read from properties of the non-generic control.
  • We wrap the real type inside a TypeDelegator that intercepts any calls to properties of the type (such as the templated containers).
  • Whenever ASP.NET requests the TemplateContainer attribute of the type, we give it back a dynamically constructed instance that contains the correct strong-typing information.

In the example code, I've created a 'GenericRepeater' that displays a strongly typed collection and the items therein. I have then sub-classed that GenericRepeater with <Object, Object> in order to create an ObjectRepeater like so:

[ControlBuilder(typeof(GenericTemplatedControlBuilder))]
[GenericControlType(typeof(GenericRepeater<,>), "CollectionTypeName", "ItemTypeName")]
public class ObjectRepeater : GenericRepeater<IEnumerable<Object>, Object>

The ObjectRepeater inherits the real generic repeater type, and the attributes tell ASP.NET to use the special control builder instead of the base one. The GenericTemplatedControlBuilder then looks for the GenericControlType attribute, and will activate the 'real' GenericRepeater instance with the two type parameters.

Inside the generic repeater implementation, we mark up the various ITemplate properties with other attributes:

[GenericTemplateContainerParameter(typeof(IndexedDataContainer<,>), 0, 1)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ItemTemplate { get; set; }

The GenericTemplateContainerParameter declaration tells the system what type of data container to instantiate, and also the indices of the generic parameters to pass down. Thus, since IndexedDataContainer takes two generic parameters, we are telling to take generic parameters 0 (TCollectionType) and 1 (TItemType) from the GenericRepeater as the relevant parameters.

When ASP.NET scans the properties of the control, we have now switched the control for a generic control and instructed our GenericControlPropertyDelegator to remap all calls for TemplateContainer to our real, generic template containers.

The Final Product

Now that we have everything in play, let's define a simple ASP.NET page that uses the control:

StrongTypingRenderedOutput.png

And, there you have it, a simple generics framework for ASP.NET that allows you to deal with pretty much any generic control scenario, and is not specifically tied down to the example for use with Repeaters.

Notes About the Code

The example code supplied does not actually implement any post-back support, so don't be surprised if you need to add some code to load/save data in the viewstate and so forth.

Additionally, if you're going to reference this code on your own Web Applications, please ensure you take the web.config elements across to register the CFC tag prefix.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Steven James Gray
Software Developer (Senior) Insurance Industry
United Kingdom United Kingdom
Member
Steve Gray is a Senior Developer at a British insurance company, working on a popular aggregator. When he's not writing ASP .NET, it's because there's SQL or WCF to write instead.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralGreat code - but some queries...memberMR_SAM_PIPER2 Feb '09 - 21:05 
First, I have to say that this is really well-written, interesting code - the use of a custom TypeDelegator for type and property interception is cool and novel to me, and the overall approach illuminates some pretty obscure parts of the ASP.NET framework, given that most of us will probably never implement our own custom data-bound templated control. It took me a long time to work out how everything you've written works together to achieve the desired outcome, and it's pretty clever.
 
My only criticism is that this solution is perhaps a little impractical for most web projects, as most sites will probably use a combination of different databound controls such as Repeater, GridView, ListView, DataList, etc, and this solution as proposed would require all of those controls to essentially be re-implemented with support for generics.
 
I personally wouldn't want the onerous task of re-building the GridView, particularly if I had to ensure compatibility with existing grids on a few hundred pages - getting compile-time support for databinding expressions is a nice-to-have, but it doesn't justify the effort required to rebuild and test a suite of controls, in my mind. However for a new site where only Repeaters were used and this technique was incumbent from the start, this could work out very well indeed.
 
I really hope that we get proper generics support in the next version of ASP.NET, as it would make everyone's life easier. I want to be able to whack a GridView<T> on a page using markup, set the item type property in markup using a typed expression, not a string, then get all the IDE goodness without writing a lick of special code.
 
Keep writing!
GeneralRe: Great code - but some queries... PinmemberSteven James Gray3 Feb '09 - 0:37 
MR_SAM_PIPER wrote:
First, I have to say that this is really well-written, interesting code - the use of a custom TypeDelegator for type and property interception is cool and novel to me, and the overall approach illuminates some pretty obscure parts of the ASP.NET framework, given that most of us will probably never implement our own custom data-bound templated control. It took me a long time to work out how everything you've written works together to achieve the desired outcome, and it's pretty clever.

 
Thanks for the feedback Sam. The key to doing this kind of work with ASP .NET really is to have a copy of Reflector installed and start digging, and once you find the relevant bits head over to MSDN's website. The trouble is that Microsoft are very keen on sealing certain parts under the hood away.
 
MR_SAM_PIPER wrote:
My only criticism is that this solution is perhaps a little impractical for most web projects, as most sites will probably use a combination of different databound controls such as Repeater, GridView, ListView, DataList, etc, and this solution as proposed would require all of those controls to essentially be re-implemented with support for generics.

 
It's admittedly impractical to go through and generics-enable each individual control, but for presentation-focused scenarios it'll work reasonably well. I might try and include generic form and list views in the next release to save people some work.
 
MR_SAM_PIPER wrote:
I really hope that we get proper generics support in the next version of ASP.NET, as it would make everyone's life easier. I want to be able to whack a GridView<T> on a page using markup, set the item type property in markup using a typed expression, not a string, then get all the IDE goodness without writing a lick of special code.

 
I originally wanted to inherit the PageBuilder (that compiles ASPX files to code) and modify the parser to support generics in the syntax, but that's not possible due to various reasons (Sealed types, internal types etc).
 
Native support for this in ASP .NET would require modifications of the CLR (To support generics on attributes, so you can say [TemplateContainer(typeof(DataContainer))], which is presently not possible, not to mention the pagebuilder syntax support required.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 29 Jan 2009
Article Copyright 2009 by Steven James Gray
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid