Click here to Skip to main content
Licence 
First Posted 10 Dec 2006
Views 32,229
Bookmarked 77 times

Making ASP.NET databinding via # work, without reflection

By | 10 Dec 2006 | Article
Describes a way to design a custom type in a way, so ASP.NET data binding syntax (DataBinder) will work without using reflection.

Introduction

There is a nice underused feature in .NET framework / ASP.NET – the TypeDescriptors mechanism. The mechanism resides in the System.ComponentModel namespace, and is generally represented by the TypeDescriptor, CustomTypeDescriptor classes, a ICustomTypeDescriptor interface, and some other supporting classes. As we can see by using the brilliant Lutz Roeder’s Reflector, these classes are mostly used by some controls (including the brand new AJAX Toolkit) and DataGrids / DataBinder.

Shortly said, this class allow us to design a type, a datasource, that when bound to a .NET control, for example, via the convenient <%# %> ASP.NET syntax, does not lead to reflection calls. This is done by providing property information dynamically, at runtime. Here’s a quick how to, with a real world scenario.

Details

In our project, we had some legacy code that returned data in some form of object[][] (actually, jagged array) which contained some metadata (columns, length, availability of next data pages, etc.). We needed to easily bind it in ASPX pages. That quickly led us to ugly things like <%# ((object[])DataItem)[3][4]%>, or something like that.

What I wanted to do was to have a custom class containing tabular data (columns and values for each row) + some custom metadata. I also wanted that class to be bindable the same way as a DataTable does, i.e.: <%# Eval(“ColumnName”) %>.

First of all, I thought that designing a simple class with an indexed property would be enough, but that led us to: <%# Eval(“[ColumnName]”) %>. Without unneeded brackets that didn’t work. That was an “OK” solution, but I was still curios: if a DataTable can do that type of thing, I should be able to do it too.

So, again, I took the reflector and went to the DataBinder class where I quickly found the following code (the GetPropertyValue method):

PropertyDescriptor descriptor1 = 
       TypeDescriptor.GetProperties(container).Find(propName, true);

//omitted check for clarity
return descriptor1.GetValue(container);

Woa! That’s not just the easy reflection way, as I always thought. Actually, as further investigation revealed, TypeDecriptor when not provided with an implementation of the ICustomTypeDescriptor interface just reverts to the plain old reflection way. So, the other part was easy -> read MSDN, see how DataTable implements ICustomTypeDescriptor, and write my own implementation for it:

//”Row” class
public class DataRetriverResultRow: CustomTypeDescriptor
{
  // Parent “DataTable” class
  private DataRetriverResultSet proxy;

  // other code omited
  public override PropertyDescriptorCollection GetProperties()
  {
   return proxy.GetPropertyDescriptorCollection(null);
  }
}

In the “DataTable” class, it's the place where actual property information is created (because all "DataTable" rows have the same properties - one for each column):

internal PropertyDescriptorCollection 
        GetPropertyDescriptorCollection(Attribute[] attributes)
  {
   if (this.propertyDescriptorCollectionCache == null)
   {
    int colCount = this.columns.Length;
    PropertyDescriptor[] descriptors = new PropertyDescriptor[colCount];
    for (int i = 0; i < colCount; i++)
    {
     descriptors[i] = new DataRetriverResultRowPropertyDescriptor(i, 
                                                   this.columns[i]);
    }
    this.propertyDescriptorCollectionCache = 
           new PropertyDescriptorCollection(descriptors);
   }
   return this.propertyDescriptorCollectionCache;
  }

And finally, the PropertyDescriptor (which is the class that actually describes a property and knows how to get a value from it). Treat it as a custom variant of the PropertyInfo class:

private class DataRetriverResultRowPropertyDescriptor : PropertyDescriptor
{
       int columnIndex; 
    
       public DataRetriverResultRowPropertyDescriptor(int index, 
                                                      string name)
        : base(name, null)
       {
        this.columnIndex = index;
       }
    
       // some dummy code implementing PropertyDescriptor ommited
    
       public override Type ComponentType
       {
        get { return typeof(DataRetriverResultRow); }
       }
    
        // component – is the object containing
        // the property, wich we are describing.  
	// So we can safely cast it and use it’s method to retrieve 
        // a value without any reflection.
       public override object GetValue(object component)
       {
           return ((DataRetriverResultRow)
                    component).GetValue(columnIndex);
       }

}

And that’s all. Basically, we just created some kind of meta descriptor which generates property info at runtime, using dynamic data (in this case, the number and order of columns in the parent “data table”).

Now, when DataBinder.Eval gets the data row of our object, it operates with a custom created and cached PropertyDescriptorCollection and uses our GetValue method, instead of PropertyInfo.GetValue. That's definitely a perfomance bonus. Also, we get a nifty syntax of databinding without a bit odd ["here goes a column name"].

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Alexander Gornik

Web Developer

Russian Federation Russian Federation

Member

I was born, live and work at Moscow, Russia. Currently, Moscow Mathematical and Physical Institute undergraduate.
 
I've started working with ASP.NET / C# / MS SQL 2.5 years ago, back in 2004.
 
Now I work as a .NET (C#) lead developer, on a big social networking site back end team. I'm mostly focused on framework design topics, perfomance optimization and all the things involving deep .NET framework knowledge.

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. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralWithout reflection PinmemberJohn Blogs15:58 29 Jun '09  
QuestionHow to use statemanagement in ASP.net in 1.1 PinmemberMember 427592823:52 1 Apr '08  
GeneralInteresting PinmemberCristian O.12:06 21 Dec '06  
GeneralImplement SetValue. PinmemberMarc Brooks10:19 12 Dec '06  
GeneralRe: Implement SetValue. PinmemberAlexander Gornik18:50 12 Dec '06  

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 11 Dec 2006
Article Copyright 2006 by Alexander Gornik
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid