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

Making ASP.NET databinding via # work, without reflection

, 10 Dec 2006
Rate this:
Please Sign up or sign in to vote.
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

Share

About the Author

Alexander Gornik
Web Developer
Russian Federation Russian Federation
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.

Comments and Discussions

 
GeneralWithout reflection PinmemberJohn Blogs29-Jun-09 16:58 
QuestionHow to use statemanagement in ASP.net in 1.1 PinmemberMember 42759282-Apr-08 0:52 
GeneralInteresting PinmemberCristian O.21-Dec-06 13:06 
GeneralImplement SetValue. PinmemberMarc Brooks12-Dec-06 11:19 
GeneralRe: Implement SetValue. PinmemberAlexander Gornik12-Dec-06 19:50 

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.

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