Click here to Skip to main content
15,883,883 members
Articles / Web Development / ASP.NET
Article

Making ASP.NET databinding via # work, without reflection

Rate me:
Please Sign up or sign in to vote.
4.69/5 (14 votes)
10 Dec 20063 min read 45.9K   79   5
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):

C#
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:

C#
//"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):

C#
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:

C#
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


Written By
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 Pin
Colin Breame29-Jun-09 15:58
Colin Breame29-Jun-09 15:58 
QuestionHow to use statemanagement in ASP.net in 1.1 Pin
Pandey Vijay Kumar S.1-Apr-08 23:52
Pandey Vijay Kumar S.1-Apr-08 23:52 
GeneralInteresting Pin
Cristian Odea21-Dec-06 12:06
Cristian Odea21-Dec-06 12:06 
GeneralImplement SetValue. Pin
Marc Brooks12-Dec-06 10:19
Marc Brooks12-Dec-06 10:19 
GeneralRe: Implement SetValue. Pin
Alexander Gornik12-Dec-06 18:50
Alexander Gornik12-Dec-06 18:50 

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

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