Attempting to use TypeDescriptors in order to facilitate greater elegance in data binding, I found them to not be working at all in an ASP.NET environment.

Working on a project for my company, I found myself in a situation where I needed to bind some data to a third party ASP.NET web control. This third party control expected the data being bound to it to conform to a specific structure and format.

I was not interested in changing parts of my data model in order to adhere to this control’s expectations, and I was definitely not interested in lugging around anything like a DataTable (unless they’re absolutely required, I hate DataTables). So, instead of worrying about any of that, I decided to change the nature of the instances of existing data through the use of custom TypeDescriptionProviders, TypeDescriptors, and PropertyDescriptors.

When all was said and done, I had created some custom collections that would register my custom type descriptors with each instance added to the collection. When binding the data to the control however, it would behave as if the normal type providers were still in place! If you ever try to exert an influence on the data binding process in ASP.NET, you too may experience an issue of it not working. I’ll detail how I ended up solving this issue in this article.

I. Registering the TypeDescriptionProvider

Let’s look at the code I was using in order to register the type descriptor with the individual instances. My goal here was to isolate the use of this custom type descriptor to the act of data binding — I didn’t want it to be present whenever I was doing whatever-else with the type. In this example, I’m working with UserProfile-typed data. The custom type description provider type I’m using is UserResultTypeDescriptionProvider. Basically, this provider is for describing UserProfile types returned as search results from a search where the profiles are ranked in terms of their relevancy to a provided search term (a name).

/// <summary>
/// Registers a custom <see cref="TypeDescriptionProvider"/> for the item, and then
/// inserts the item into the collection at the specified index.
/// </summary>
/// <param name="index">The zero-based index at which item should be inserted.</param>
/// <param name="item">The object to insert. The value can be null for reference types.</param>
protected override void InsertItem(int index, UserProfile item)
{
  TypeDescriptor.AddProvider(new UserResultTypeDescriptionProvider(), item);

  base.InsertItem(index, item);
}

The custom type description provider shown above would then return a specific type of TypeDescriptor containing exposing a static array of types and instance property readers/writers that would allow the data to achieve conformance with the third party control’s data binding expectations. The implementation isn’t important, however. We can clearly see I’m registering the custom TypeDescriptionProvider with the instance; that’s good enough for the purposes of this article.

II. Binding to a DataBoundControl

The third party control offered the typical kind of data binding functionality exposed by all DataBoundControl-derived classes: Data would be attached to the DataSource property, a call would be made to DataBind, and then everything would be golden. Why would it be then, that instead of using the custom TypeDescriptionProvider/TypeDescriptor for the UserProfile type, it would end up using the default TypeDescriptionProvider/TypeDescriptor?

The reason why has to do with a very recent change to the ASP.NET framework that was made in the .NET 4.0 framework. The specific change I’m referring to was made to the DataBinder ASP.NET class, in the GetPropertyValue method. A data binding control typically will provide GetPropertyValue with the object instance and the name of the property it wants the value for. It is here, in the GetPropertyValue method, where TypeDescriptors come into play when discussing data binding.

ASP.NET returns the value by making a call to TypeDescriptor.GetProperties; a call which will give control to a custom type descriptor if one was registered to the instance or instance type. At least, that’s what it used to do more or less before .NET 4.0.

Starting in .NET 4.0, DataBinder.GetPropertyValue now makes a call to an internal method named GetPropertiesFromCache. In this new method, ASP.NET makes use of cached PropertyDescriptorCollections in order to retrieve the values from provided instances. If the proper PropertyDescriptorCollection is not available at the time of execution, then it would retrieve the appropriate TypeDescriptor, invoke GetProperties, and cache the result of that. Smart optimization; however, in my opinion, it wasn’t implemented correctly, because although the scenario described above sounds reasonable and logical, it would end up not using my custom type descriptor and deferring to the default descriptor.

How isn’t GetPropertiesFromCache implemented correctly? In order to make use of a custom type descriptor and custom property descriptors, you would need to call TypeDescriptor.GetProperties(profile), where profile is an instance of my UserProfile type. What GetPropertiesFromCache does is very different. Instead, it gets the type of the provided container first, consequently making use of a TypeDescriptor based on the provided object’ type and not the instance itself. You may remember that I earlier indicated that I wanted to isolate this custom TypeDescriptor to data binding only. Thus, registering a type descriptor to the UserProfile type itself was out of the question. Therefore, my custom type descriptor was going to get ignored here.

I think that’s a mistake in the .NET source code. Luckily for us, that’s not all the GetPropertiesFromCache method does. Before any of the above gets executed, it checks if the EnableCaching property is enabled, or if the provided container implements ICustomTypeDescriptor. The desired custom TypeDescriptor we’re looking for here implements ICustomTypeDescriptor, but the the provided UserProfile instance certainly does not. This leaves us with the EnableCaching property.

III. The Solution

I solved my problem by setting EnableCaching to false on the third party control.

I’ve read (didn’t spend much time doing so) that the EnableCaching property is disabled by default. In my case, it clearly was enabled by default. I am unaware if that’s a mistake made by the specific control, or if caching is actually enabled by default.

To sum up, if you make use of custom TypeDescriptionProviders, TypeDescriptors, and PropertyDescriptors when data binding to an ASP.NET control using .NET 4.0, and if you register these custom descriptors on the instance (not type) level, then you’ll want to make sure EnableCaching is disabled.

Matt Weber

I'm the founder of Bad Echo LLC, which offers consulting services to clients who need an expert in C#, WPF, Outlook, and other advanced .NET related areas. I enjoy well-designed code, independent thought, and the application of rationality in general. You can reach me at matt@badecho.com.

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 
   
© 2012-2013 Matt Weber. All Rights Reserved. Terms of Use.