ExpandoObject: exposing properties to the TypeDescriptor

Consider the following unit test:

image

I’ve highlighted the interesting parts which are:

There should be two properties but sadly this unit test returns:

image

The problem is that the ExpandoObject, internally, uses a dictionary of string and object (IDictionary<string,object>) to store the dynamically added properties.

A while ago I had to deal with this exact problem where an external library I was using excepted object as input to a method and internally used the TypeDescriptor to get the properties out of the object, which didn’t work at all as I proved with the unit test just now.

What to do?

As always I turned to stackoverflow for support…and the following is just a summary of the response I got.

Since the properties I want to expose from the ExpandoObject live inside the internal dictionary, all I need to do is to tell the TypeDescriptor how to pull those out when it’s working with an ExpandoObject.
That is accomplished via a custom type descriptor such as the following:
note: this is just a brief summary (complete code can be found here)

public class ExpandoObjectTypeDescriptor : ICustomTypeDescriptor
{
        private readonly IDictionary<string,object> m_Instance;

        public ExpandoObjectTypeDescriptor(dynamic instance)
        {
            m_Instance = instance as IDictionary<string, object>;
        }

------

        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return new PropertyDescriptorCollection(
                m_Instance.Keys
                          .Select(x => new ExpandoObjectPropertyDescriptor(m_Instance, x))
                          .ToArray<PropertyDescriptor>());
        }

------

class ExpandoObjectPropertyDescriptor : PropertyDescriptor
{
            private readonly IDictionary<string, object> m_Instance;
            private readonly string m_Name;

            public ExpandoObjectPropertyDescriptor(IDictionary<string, object> instance, string name)
                : base(name, null)
            {
                m_Instance = instance;
                m_Name = name;
            }

-----

            public override object GetValue(object component)
            {
                return m_Instance[m_Name];
            }

I’ve highlighted a couple of things:

  • line 12: how to get the properties by returning a custom PropertyDescriptor for each item in the dictionary.
  • line 22: the custom wrapper class that reads items out of the dictionary and exposes them as properties.

Ok, next we need to do is tell the TypeDescriptor about our custom class which is done through a provider:
note: this is just a brief summary (complete code can be found here)

public class ExpandoObjectTypeDescriptionProvider : TypeDescriptionProvider
    {
        private static readonly TypeDescriptionProvider m_Default = TypeDescriptor.GetProvider(typeof(ExpandoObject));

        public ExpandoObjectTypeDescriptionProvider()
            :base(m_Default)
        {
        }

        public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
        {
            var defaultDescriptor = base.GetTypeDescriptor(objectType, instance);

            return instance == null ? defaultDescriptor :
                       new ExpandoObjectTypeDescriptor(instance);
        }
    }

Finally, all that’s left is to tell the TypeDescriptor when we want to user our custom provider.

Option 1: attach custom provider to the ExpandoObject instance (one instance only)

image

Here, only the one instance (“Dynamic”) is affected.

Option 2: attach custom provider to the ExpandoObject type (all instances)

image

Here, we attach it to the ExpandoObject type in a central place. This being an MSpec unit test results in me implementing the IAssemblyContext to accomplish that. What you do is…well up to you.

and with that…TADA!

image

Lastly, I’ve uploaded the complete code to GitHub.

, , , , ,

  1. #1 by tnejohnson on August 12, 2015 - 05:18

    Thanks. You don’t need to implement ICustomTypeDescriptor and provide all that boilerplate functionality, though; you can just inherit from CustomTypeDescriptor.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: