Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Creating a dynamic, extensible C# Expando Object


:P
On this page:
continued from Page 1

I love dynamic functionality in a strongly typed language because it offers us the best of both worlds. In C# (or any of the main .NET languages) we now have the dynamic type that provides a host of dynamic features for the static C# language.

One place where I've found dynamic to be incredibly useful is in building extensible types or types that expose traditionally non-object data (like dictionaries) in easier to use and more readable syntax. I wrote about a couple of these for accessing old school ADO.NET DataRows and DataReaders more easily for example. These classes are dynamic wrappers that provide easier syntax and auto-type conversions which greatly simplifies code clutter and increases clarity in existing code.

ExpandoObject in .NET 4.0

Another great use case for dynamic objects is the ability to create extensible objects - objects that start out with a set of static members and then can add additional properties and even methods dynamically. The .NET 4.0 framework actually includes an ExpandoObject class which provides a very dynamic object that allows you to add properties and methods on the fly and then access them again.

For example with ExpandoObject you can do stuff like this:

dynamic expand = new ExpandoObject();

expand.Name = "Rick";
expand.HelloWorld = (Func<string, string>) ((string name) => 
{ 
    return "Hello " + name; 
});

Console.WriteLine(expand.Name);
Console.WriteLine(expand.HelloWorld("Dufus"));

Internally ExpandoObject uses a Dictionary like structure and interface to store properties and methods and then allows you to add and access properties and methods easily. As cool as ExpandoObject is it has a few shortcomings too:

  • It's a sealed type so you can't use it as a base class
  • It only works off 'properties' in the internal Dictionary - you can't expose existing type data
  • It doesn't serialize to XML or with DataContractSerializer/DataContractJsonSerializer

Expando - A truly extensible Object

ExpandoObject is nice if you just need a dynamic container for a dictionary like structure. However, if you want to build an extensible object that starts out with a set of strongly typed properties and then allows you to extend it, ExpandoObject does not work because it's a sealed class that can't be inherited.

I started thinking about this very scenario for one of my applications I'm building for a customer. In this system we are connecting to various different user stores. Each user store has the same basic requirements for username, password, name etc. But then each store also has a number of extended properties that is available to each application. In the real world scenario the data is loaded from the database in a data reader and the known properties are assigned from the known fields in the database. All unknown fields are then 'added' to the expando object dynamically.

In the past I've done this very thing with a separate property - Properties - just like I do for this class. But the property and dictionary syntax is not ideal and tedious to work with.

I started thinking about how to represent these extra property structures. One way certainly would be to add a Dictionary, or an ExpandoObject to hold all those extra properties. But wouldn't it be nice if the application could actually extend an existing object that looks something like this as you can with the Expando object:

public class User : Westwind.Utilities.Dynamic.Expando
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string Name { get; set; }
    public bool Active { get; set; }
    public DateTime? ExpiresOn { get; set; }
}

and then simply start extending the properties of this object dynamically? Using the Expando object I describe later you can now do the following:

[TestMethod]
public void UserExampleTest()
{            
    var user = new User();

    // Set strongly typed properties
    user.Email = "rick@west-wind.com";
    user.Password = "nonya123";
    user.Name = "Rickochet";
    user.Active = true;

    // Now add dynamic properties
    dynamic duser = user;
    duser.Entered = DateTime.Now;
    duser.Accesses = 1;

    // you can also add dynamic props via indexer
    user["NickName"] = "AntiSocialX";
    duser["WebSite"] = "http://www.west-wind.com/weblog";


    // Access strong type through dynamic ref
    Assert.AreEqual(user.Name,duser.Name);

    // Access strong type through indexer 
    Assert.AreEqual(user.Password,user["Password"]);
    

    // access dyanmically added value through indexer
    Assert.AreEqual(duser.Entered,user["Entered"]);
    
    // access index added value through dynamic
    Assert.AreEqual(user["NickName"],duser.NickName);
    

    // loop through all properties dynamic AND strong type properties (true)
    foreach (var prop in user.GetProperties(true))
    { 
        object val = prop.Value;
        if (val == null)
            val = "null";

        Console.WriteLine(prop.Key + ": " + val.ToString());
    }
}

As you can see this code somewhat blurs the line between a static and dynamic type. You start with a strongly typed object that has a fixed set of properties. You can then cast the object to dynamic (as I discussed in my last post) and add additional properties to the object. You can also use an indexer to add dynamic properties to the object.

To access the strongly typed properties you can use either the strongly typed instance, the indexer or the dynamic cast of the object. Personally I think it's kinda cool to have an easy way to access strongly typed properties by string which can make some data scenarios much easier.

To access the 'dynamically added' properties you can use either the indexer on the strongly typed object, or property syntax on the dynamic cast.

Using the dynamic type allows all three modes to work on both strongly typed and dynamic properties.

Finally you can iterate over all properties, both dynamic and strongly typed if you chose. Lots of flexibility.

Note also that by default the Expando object works against the (this) instance meaning it extends the current object. You can also pass in a separate instance to the constructor in which case that object will be used to iterate over to find properties rather than this.

Using this approach provides some really interesting functionality when use the dynamic type. To use this we have to add an explicit constructor to the Expando subclass:

public class User : Westwind.Utilities.Dynamic.Expando
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string Name { get; set; }
    public bool Active { get; set; }
    public DateTime? ExpiresOn { get; set; }

    public User() : base()
    { }

    // only required if you want to mix in seperate instance
    public User(object instance)
        : base(instance)
    {
    }
}

to allow the instance to be passed. When you do you can now do:

[TestMethod]
public void ExpandoMixinTest()
{
    // have Expando work on Addresses
    var user = new User( new Address() );

    // cast to dynamicAccessToPropertyTest
    dynamic duser = user;

    // Set strongly typed properties
    duser.Email = "rick@west-wind.com";
    user.Password = "nonya123";
    
    // Set properties on address object
    duser.Address = "32 Kaiea";
    //duser.Phone = "808-123-2131";

    // set dynamic properties
    duser.NonExistantProperty = "This works too";

    // shows default value Address.Phone value
    Console.WriteLine(duser.Phone);

}


Using the dynamic cast in this case allows you to access *three* different 'objects': The strong type properties, the dynamically added properties in the dictionary and the properties of the instance passed in! Effectively this gives you a way to simulate multiple inheritance (which is scary - so be very careful with this, but you can do it).

How Expando works

Behind the scenes Expando is a DynamicObject subclass as I discussed in my last post. By implementing a few of DynamicObject's methods you can basically create a type that can trap 'property missing' and 'method missing' operations. When you access a non-existant property a known method is fired that our code can intercept and provide a value for. Internally Expando uses a custom dictionary implementation to hold the dynamic properties you might add to your expandable object.

Let's look at code first. The code for the Expando type is straight forward and given what it provides relatively short. Here it is.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Dynamic;
using System.Reflection;

namespace Westwind.Utilities.Dynamic
{
    /// <summary>
    /// Class that provides extensible properties and methods. This
    /// dynamic object stores 'extra' properties in a dictionary or
    /// checks the actual properties of the instance.
    /// 
    /// This means you can subclass this expando and retrieve either
    /// native properties or properties from values in the dictionary.
    /// 
    /// This type allows you three ways to access its properties:
    /// 
    /// Directly: any explicitly declared properties are accessible
    /// Dynamic: dynamic cast allows access to dictionary and native properties/methods
    /// Dictionary: Any of the extended properties are accessible via IDictionary interface
    /// </summary>
    [Serializable]
    public class Expando : DynamicObject, IDynamicMetaObjectProvider
    {
        /// <summary>
        /// Instance of object passed in
        /// </summary>
        object Instance;

        /// <summary>
        /// Cached type of the instance
        /// </summary>
        Type InstanceType;

        PropertyInfo[] InstancePropertyInfo
        {
            get
            {
                if (_InstancePropertyInfo == null && Instance != null)                
                    _InstancePropertyInfo = Instance.GetType().GetProperties(BindingFlags.Instance |                                                           BindingFlags.Public | BindingFlags.DeclaredOnly);
                return _InstancePropertyInfo;                
            }
        }
        PropertyInfo[] _InstancePropertyInfo;


        /// <summary>
        /// String Dictionary that contains the extra dynamic values
        /// stored on this object/instance
        /// </summary>        
        /// <remarks>Using PropertyBag to support XML Serialization of the dictionary</remarks>
        public PropertyBag Properties = new PropertyBag();

        //public Dictionary<string,object> Properties = new Dictionary<string, object>();

        /// <summary>
        /// This constructor just works off the internal dictionary and any 
        /// public properties of this object.
        /// 
        /// Note you can subclass Expando.
        /// </summary>
        public Expando() 
        {
            Initialize(this);            
        }

        /// <summary>
        /// Allows passing in an existing instance variable to 'extend'.        
        /// </summary>
        /// <remarks>
        /// You can pass in null here if you don't want to 
        /// check native properties and only check the Dictionary!
        /// </remarks>
        /// <param name="instance"></param>
        public Expando(object instance)
        {
            Initialize(instance);
        }


        protected virtual void Initialize(object instance)
        {
            Instance = instance;
            if (instance != null)
                InstanceType = instance.GetType();           
        }


       /// <summary>
       /// Try to retrieve a member by name first from instance properties
       /// followed by the collection entries.
       /// </summary>
       /// <param name="binder"></param>
       /// <param name="result"></param>
       /// <returns></returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = null;

            // first check the Properties collection for member
            if (Properties.Keys.Contains(binder.Name))
            {
                result = Properties[binder.Name];
                return true;
            }


            // Next check for Public properties via Reflection
            if (Instance != null)
            {
                try
                {
                    return GetProperty(Instance, binder.Name, out result);                    
                }
                catch { }
            }

            // failed to retrieve a property
            result = null;
            return false;
        }


        /// <summary>
        /// Property setter implementation tries to retrieve value from instance 
        /// first then into this object
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {

            // first check to see if there's a native property to set
            if (Instance != null)
            {
                try
                {
                    bool result = SetProperty(Instance, binder.Name, value);
                    if (result)
                        return true;
                }
                catch { }
            }
            
            // no match - set or add to dictionary
            Properties[binder.Name] = value;
            return true;
        }

        /// <summary>
        /// Dynamic invocation method. Currently allows only for Reflection based
        /// operation (no ability to add methods dynamically).
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            if (Instance != null)
            {
                try
                {
                    // check instance passed in for methods to invoke
                    if (InvokeMethod(Instance, binder.Name, args, out result))
                        return true;                    
                }
                catch { }
            }

            result = null;
            return false;
        }
        

        /// <summary>
        /// Reflection Helper method to retrieve a property
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="name"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        protected bool GetProperty(object instance, string name, out object result)
        {
            if (instance == null)
                instance = this;

            var miArray = InstanceType.GetMember(name, BindingFlags.Public |                                       BindingFlags.GetProperty | BindingFlags.Instance);
            if (miArray != null && miArray.Length > 0)
            {
                var mi = miArray[0];
                if (mi.MemberType == MemberTypes.Property)
                {
                    result = ((PropertyInfo)mi).GetValue(instance,null);
                    return true;
                }
            }

            result = null;
            return false;                
        }

        /// <summary>
        /// Reflection helper method to set a property value
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected bool SetProperty(object instance, string name, object value)
        {
            if (instance == null)
                instance = this;

            var miArray = InstanceType.GetMember(name, BindingFlags.Public |                                                        BindingFlags.SetProperty |                                                        BindingFlags.Instance);
            if (miArray != null && miArray.Length > 0)
            {
                var mi = miArray[0];
                if (mi.MemberType == MemberTypes.Property)
                {
                    ((PropertyInfo)mi).SetValue(Instance, value, null);
                    return true;
                }
            }
            return false;                
        }

        /// <summary>
        /// Reflection helper method to invoke a method
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="name"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        protected bool InvokeMethod(object instance, string name, object[] args, out object result)
        {
            if (instance == null)
                instance = this;

            // Look at the instanceType
            var miArray = InstanceType.GetMember(name,
                                    BindingFlags.InvokeMethod |
                                    BindingFlags.Public | BindingFlags.Instance);

            if (miArray != null && miArray.Length > 0)
            {
                var mi = miArray[0] as MethodInfo;
                result = mi.Invoke(Instance, args);
                return true;
            }

            result = null;
            return false;
        }



        /// <summary>
        /// Convenience method that provides a string Indexer 
        /// to the Properties collection AND the strongly typed
        /// properties of the object by name.
        /// 
        /// // dynamic
        /// exp["Address"] = "112 nowhere lane"; 
        /// // strong
        /// var name = exp["StronglyTypedProperty"] as string; 
        /// </summary>
        /// <remarks>
        /// The getter checks the Properties dictionary first
        /// then looks in PropertyInfo for properties.
        /// The setter checks the instance properties before
        /// checking the Properties dictionary.
        /// </remarks>
        /// <param name="key"></param>
        /// 
        /// <returns></returns>
        public object this[string key]
        {
            get
            {
                try
                {
                    // try to get from properties collection first
                    return Properties[key];
                }
                catch (KeyNotFoundException ex)
                {
                    // try reflection on instanceType
                    object result = null;
                    if (GetProperty(Instance, key, out result))
                        return result;

                    // nope doesn't exist
                    throw;
                }
            }
            set
            {
                if (Properties.ContainsKey(key))
                {
                    Properties[key] = value;
                    return;
                }

                // check instance for existance of type first
                var miArray = InstanceType.GetMember(key, BindingFlags.Public | BindingFlags.GetProperty);
                if (miArray != null && miArray.Length > 0)
                    SetProperty(Instance, key, value);
                else
                    Properties[key] = value;
            }
        }


        /// <summary>
        /// Returns and the properties of 
        /// </summary>
        /// <param name="includeProperties"></param>
        /// <returns></returns>
        public IEnumerable<KeyValuePair<string,object>> GetProperties(bool includeInstanceProperties = false)
        {
            if (includeInstanceProperties && Instance != null)
            {
                foreach (var prop in this.InstancePropertyInfo)
                    yield return new KeyValuePair<string, object>(prop.Name, prop.GetValue(Instance, null));
            }

            foreach (var key in this.Properties.Keys)
               yield return new KeyValuePair<string, object>(key, this.Properties[key]);

        }
  

        /// <summary>
        /// Checks whether a property exists in the Property collection
        /// or as a property on the instance
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Contains(KeyValuePair<string, object> item, bool includeInstanceProperties = false)
        {
            bool res = Properties.ContainsKey(item.Key);
            if (res)
                return true;

            if (includeInstanceProperties && Instance != null)
            {
                foreach (var prop in this.InstancePropertyInfo)
                {
                    if (prop.Name == item.Key)
                        return true;
                }
            }

            return false;
        }        

    }
}

Although the Expando class supports an indexer, it doesn't actually implement IDictionary or even IEnumerable. It only provides the indexer and Contains() and GetProperties() methods, that work against the Properties dictionary AND the internal instance.

The reason for not implementing IDictionary is that a) it doesn't add much value since you can access the Properties dictionary directly and that b) I wanted to keep the interface to class very lean so that it can serve as an entity type if desired. Implementing these IDictionary (or even IEnumerable) causes LINQ extension methods to pop up on the type which obscures the property interface and would only confuse the purpose of the type. IDictionary and IEnumerable are also problematic for XML and JSON Serialization - the XML Serializer doesn't serialize IDictionary<string,object>, nor does the DataContractSerializer. The JavaScriptSerializer does serialize, but it treats the entire object like a dictionary and doesn't serialize the strongly typed properties of the type, only the dictionary values which is also not desirable. Hence the decision to stick with only implementing the indexer to support the user["CustomProperty"] functionality and leaving iteration functions to the publicly exposed Properties dictionary.

Note that the Dictionary used here is a custom PropertyBag class I created to allow for serialization to work. One important aspect for my apps is that whatever custom properties get added they have to be accessible to AJAX clients since the particular app I'm working on is a SIngle Page Web app where most of the Web access is through JSON AJAX calls. PropertyBag can serialize to XML and one way serialize to JSON using the JavaScript serializer (not the DCS serializers though).

The key components that make Expando work in this code are the Properties Dictionary and the TryGetMember() and TrySetMember() methods. The Properties collection is public so if you choose you can explicitly access the collection to get better performance or to manipulate the members in internal code (like loading up dynamic values form a database).

Notice that TryGetMember() and TrySetMember() both work against the dictionary AND the internal instance to retrieve and set properties. This means that user["Name"] works against native properties of the object as does user["Name"] = "RogaDugDog".

What's your Use Case?

This is still an early prototype but I've plugged it into one of my customer's applications and so far it's working very well. The key features for me were the ability to easily extend the type with values coming from a database and exposing those values in a nice and easy to use manner. I'm also finding that using this type of object for ViewModels works very well to add custom properties to view models. I suspect there will be lots of uses for this - I've been using the extra dictionary approach to extensibility for years - using a dynamic type to make the syntax cleaner is just a bonus here.

What can you think of to use this for?

Resources

Posted in CSharp  .NET  Dynamic Types  

The Voices of Reason


 

John Atten
February 08, 2012

# re: Creating a dynamic, extensible C# Expando Object

Fantastic post! Thanks.

I am having to learn all this on my own, and this is exactly the kin thing I find most helpful. Helps me understand what happens "inside the black box" so to speak.

What would I use this for? I'll get back to you after I have mastered it a little.

#Subscribed. :-)

Mark
February 08, 2012

# re: Creating a dynamic, extensible C# Expando Object

Great article Rick, IMHO dynamics are convenient, but not much more than that. I sometimes dynamically add when using Javascript, but I don't see the trade off of the convenience versus power (losing LINQ like stuff).

A similar case can be made for the recent trend of NoSQL, yes it has it's place, but I believe it is more niche cases when considering the need "dynamically" add properties on certain objects. A good model done from the get-go is a better approach.

Convenient - yes, practical - no. I'd be interested to hear other use-cases, maybe I'm missing some good functionality that can be done with dynamics.

Bertrand Le Roy
February 08, 2012

# re: Creating a dynamic, extensible C# Expando Object

Rick, did you check out Clay? http://clay.codeplex.com/ That's what we use in Orchard for all our dynamic object needs (and we have a lot). It's very extensible and has fantastic object initialization syntax.

Rick Strahl
February 08, 2012

# re: Creating a dynamic, extensible C# Expando Object

@Bertrand - I started to but didn't get around to playing with it yet - this post was already written up :-). Did see your post on it though and will definitely take a closer look when I get a chance.

Rick Strahl
February 08, 2012

# re: Creating a dynamic, extensible C# Expando Object

@Mark - I don't agree that dynamics are more than a niche. I think with a static language you tend to make yourself think about problems in a rigid non-changing fashion with things like ORMs and other auto-mapping tools. While it's possible to extend these (with dictionaries for example), the syntax is often undesirable and requires special code to manage the extra data. Dynamics can make that code more natural and take away some of the tedium like type casting that you otherwise have to do. The downside is that dynamic data has a bit of overhead although for things like dictionaries it's not too bad.

ms440
February 09, 2012

# re: Creating a dynamic, extensible C# Expando Object

@Rick. Thanks!

Great article as always. I used Clay (tnx to Bertrand and his team), but in my latest project I'm trying ImpromptuInterface (take a look at http://code.google.com/p/impromptu-interface/) it is much faster and, while not as rich as Clay (IMHO), gives me the functionality I really need in my project.

Jim Z.
February 09, 2012

# re: Creating a dynamic, extensible C# Expando Object

Can you explain a little more about using the new constructor, passing in "new Address()"? Reading the code it looked like Address was a class (defined elsewhere I guess), and this created a Property on User called Address. But the code uses Address as just a string. I was expecting something like duser.Address.City = "Maui" or something like that, where the Address class had a City property. I'm confused.

SciFi Geek
February 09, 2012

# re: Creating a dynamic, extensible C# Expando Object

What kind of data store and data access technology are you using with these Expandos?

Rick Strahl
February 09, 2012

# re: Creating a dynamic, extensible C# Expando Object

@Jim - The constructor supports an object instance that can be passed in. Basically that object's properties become available on the dynamic type. So if you Address with properties of StreetAddress, Phone you can do:

Address address = new Address();
address.StreetAddress  = "New Street Address";
 
dynamic user = new User( address );
 
// static property of User class
string name = user.Name;  
 
// static property of Address class
string street = user.StreetAddress;    // "New Street Address"


In essence you can mix-in a second type into your object data - the properties are effectively merged.

The order is:
* Native strong type properties are loaded first (by the DLR)
* Dynamically added properties are loaded next (by DynamicObject overrides)
* Instance properties are loaded last

In theory this could be expanded to mix in a bunch of objects that are checked for properties and extend the original type.

This is a bit like jQuery.extend() which merges two types into one.

Jamie
February 10, 2012

# re: Creating a dynamic, extensible C# Expando Object

I got into this a bit myself, actually while writing a C# version of JQuery.extend() but I could never quite resolve one issue and was wondering if your experience could shed some light on this. (Detailed info on the internals of dynamic types seems tough to come by on the web!)

The GetMemberBinder parameter returned by TryGetMember has this property "ReturnType" which would seem to be extraordinarily useful, if it would let you know at run time the type of the target of the assignment. In practice it seems to always return "object". The only thing I could find about this is that it has to do with complexities of the CLR and the code can't find out the return type at that point in the execution for reasons I did not quite understand.

Is there any way to determine at run time the target type of an expando object Get? This would let you do miraculous things like:

dynamic test = new ExpandoObject();
test.stringVal = "12";
int intVal = test.stringVal;


Which, unfortunately, will fail at runtime (with the native ExpandoObject anyway). This means that in practice, I can't use the best part of dynamics sometimes, since the whole reason for using them in the first place is, er, to deal with stuff you don't know about. At least for me - I use them pretty much exclusively for dealing with data from javascript/JSON.

So I've resorted to implementing a DynamicObject with a Get<T> method and coding this like:

int intVal = test.Get<int>("stringVal");  // or


Which, of course, kind of defeats the purpose in my mind, if I have to substitute a lot of ugly type conversion code for creating a lot of single use classes.

Rick Strahl
February 13, 2012

# re: Creating a dynamic, extensible C# Expando Object

@Jamie - Ah I see what you're trying to do. The issue is that the runtime binder doesn't know what to bind to until the value comes back. I think custom implementations of IDynamicMetaObject can potentially provide the target type, but DynamicObject doesn't provide it.

Type conversions should work however if .NET supports implicit conversions. Casting from int to decimal for example should work.

This works:
dynamic intVal = 10;
decimal decVal = intVal;
Console.WriteLine(decVal);

But this doesn't:

dynamic intVal = 10;
string stringVal = intVal;
Console.WriteLine(stringVal);

because it's an explicit conversion from int to string. You should be able to do things like direct assignments to an interface or parent class without problems as well.

So, what's the use case with this? This is one thing I would rather avoid. I know you can do this in JavaScript and most other true dynamic languages, but that's one feature that's not desirable IMHO. And if you are using explicit conversions then I think you should also be explicit in your conversion code.

Marty
August 07, 2012

# re: Creating a dynamic, extensible C# Expando Object

Hi,

Speaking of use case`s, what I'm trying to do is create a Dynamic Contact list.
That has a strongly typed base class "Contact" with props like Name, Sex, Phone
and a "Profile" class, that has props like ContactId, PropName, PropVal to enable storage of unlimited number of Contact attributes.

Your approach seems to be a good start for a POCO class, that would later on map to the described entity structure.

My question is - How do You think, would it be possible to achieve a reasonable result that would allow querying those dynamic properties, so it woul look like this :

DynamicContact.Where(p=>p.SomeProperty = "1" && p.SomeOtherProperty = 5)

Boris B.
September 15, 2012

# re: Creating a dynamic, extensible C# Expando Object

Nice, I've been searching for a generic way to create ViewModels that extend base models with additional properties and behavior when this blog came up.

A suggestion, why not make your Expando generic, as in Expando<TBase>, that way you can store PropertyInfo[] and such in a static field (in a generic class each closed type would gets it's separate static), since property and method infos should be same for all instances of the same base object type? Then you could replace every GetType() with typeof(TBase) which would then resolve the type at compiletime.

And a question, does this work with existing databinding mechanisms for WPF and/or WinForms, i.e. could you stuff this Expando in a BindingSource and bind controls to that BindingSource (and use those new, dynamic properties)?

Rick Strahl
October 22, 2012

# re: Creating a dynamic, extensible C# Expando Object

@Boris - Databinding unfortunately can be problematic. I haven't tried with WPF or WinForms but I suspect it won't because they use Reflection to pick up properties and don't look at the dynamic type interfaces that provide the properties. That's definitely a limitation.

David P.
January 18, 2013

# re: Creating a dynamic, extensible C# Expando Object

This was an interesting article, but my use case is similar to Marty's. I started out by using the dbContext generator for persistent ignorant entity classes for my Database tables. I have a fluctuating amount on extended properties that can be known at runtime by doing a db lookup. For data retrieval I have a stored proc that does a dynamic PIVOT to retrun a crosstab resultset, with all these extendable columns in it. I would like to get a List of these returned, such as:

ExtendBaseClass(typeof(MyTable));
List<MyTableExtended> lList = dbContext.Database.SqlQuery<MyTableExtended>("EXEC spGetMyTable @Param1,new SqlParameter { ParameterName = "Param1", DbType = System.Data.DbType.String, Value = "Bob" }).ToList<MyTableExtended>();
 
public void ExtendBaseClass(Type baseClass)
{
      // Run code to add extended properties
}


Is something like this possible?

:) David

Peter
January 31, 2013

# re: Creating a dynamic, extensible C# Expando Object

Hi Rick,

Cool article. Thanks!

I have a question about JSON serialization of the Expando from ASP.NET. When using a default webmethod and json serialization the result after serializing is <variable>.Properties.<property_A>.
However when using it in javascript, the existence of the Properties intermediate property is not really useful. What are your thoughts on "getting rid of it" when serializing to JSON?

Thank you,

Peter

Rick Strahl
January 31, 2013

# re: Creating a dynamic, extensible C# Expando Object

@Peter - I think you should be able to do [XmlIgnore][NonSerialized] combo to have the JSON serializers ignore it. Haven't tried this though. But... wouldn't you want the properties? How else would this object serialize, since all state is in the properties dictionary?

nick
January 27, 2015

# re: Creating a dynamic, extensible C# Expando Object

expando + dynamic really helps simplify my code in json world. I use it plenty now, though sometimes I feel like cheating, because it is late-binding, no?

cheng
January 27, 2015

# re: Creating a dynamic, extensible C# Expando Object

any alternative to using PropertyBag? Trying to use this code in webform project, not mvc

Chief
March 20, 2015

# re: Creating a dynamic, extensible C# Expando Object

I am using the Expando object to facilitate an adhoc data query in my application where the returned data is a result of a sql query built during run time. Not tied to strong typed is so very VFP like.

Thank you.

Rick Strahl
March 20, 2015

# re: Creating a dynamic, extensible C# Expando Object

@Chief - I wrote about just that thing shortly after this post was made

http://weblog.west-wind.com/posts/2011/Dec/06/Creating-a-Dynamic-DataReader-for-easier-Property-Access

There is code for that and also a dynamic DataRow reader for DataTables in Westwind.Utilities here:

Dynamic DataReader:
https://github.com/RickStrahl/WestwindToolkit/blob/master/Westwind.Utilities/Data/DynamicDataReader.cs

Dynamic DataRow:
https://github.com/RickStrahl/WestwindToolkit/blob/master/Westwind.Utilities/Data/DynamicDataRow.cs

Kam Minter
May 13, 2015

# re: Creating a dynamic, extensible C# Expando Object

Rick,

I have a question, can this be used in a wpf program? The reason I ask is that I want to basically put together two tables with strongly typed properties and with a couple of dynamic properties.

Rick Strahl
May 13, 2015

# re: Creating a dynamic, extensible C# Expando Object

Sure it should work in any .NET code.

Nikola
June 18, 2015

# re: Creating a dynamic, extensible C# Expando Object

Hi, i've tried out your expando object and it hold nicely, however it's not quite what i'm looking for. I need to be able to see when properties are changed so it can be usable with WPF. Is that even possible?

Matt
August 04, 2015

# re: Creating a dynamic, extensible C# Expando Object

Thanks Rick. Your Expando object looks to be exactly what I'm after.
However, I'm having trouble deserializing. The Title and FullName properties are null after deserialization.
Can you see what I'm doing wrong?

    public class AnswerFile : Expando
    {
        public string Title { get; set; }
        public string FullName { get; set; }
 
        public AnswerFile() : base() { }
        // only required if you want to mix in seperate instance
        public AnswerFile(object instance) : base(instance) { }
    }
 
    [Test]
    public void SerializationTest()
    {
        var details = new AnswerFile();
        details.Title = "d";
        details.FullName = "d";
 
        dynamic ddetails = details;
        ddetails.Greeting = "Hello";
 
        details["prop1"] = "prop1value";
        ddetails["prop2"] = "prop2value";
 
        var xml = SerializationUtils.SerializeObjectToString(details);
        SerializationUtils.SerializeObject(details, @"C:\Users\Matthew\AppData\Local\Temp\OEH\SerializeObject1.xml", false);
 
        var obj1 = SerializationUtils.DeSerializeObject(xml, details.GetType()) as AnswerFile;
        var obj2 = SerializationUtils.DeSerializeObject(@"C:\Users\Matthew\AppData\Local\Temp\OEH\SerializeObject1.xml", details.GetType(), false, true) as AnswerFile;
    }

dd
January 03, 2016

# re: Creating a dynamic, extensible C# Expando Object

Can methods dynamically added to a "dynamic" use keyword *this* from within body of the method?

For example
dynamic d = src; // Src subclass from "Expando"

Instead of:
d.SomeMethod = (Func<dynamic, bool>) (dynamic item) => (
{
var abc=item.xyz;
...
}
);
d.SomeMethod(d);

Do this instead:
d.SomeMethod = (Func<bool>) () => (
{
var abc=this.xyz;
...
}
);
d.SomeMethod(); // no need pass any input argument

Spencer Bench
June 30, 2016

# re: Creating a dynamic, extensible C# Expando Object

@dd: I don't think so. At least, not in the way you're illustrating. In lambda expressions, "this" is a captured variable. For example:
public class MyClass {
 
    public void MyMethod() {
        dynamic d = new SpecialDynamicObject();
        d.xyz = true;
        d.SpecialMethod = () => {
            return this.xyz; // Won't compile; "MyClass" has no member "xyz"
        };
    }
}

The "this" keyword in the body of the lambda expression refers to the enclosing MyClass instance, not the local variable d. If you need to refer to d instead, you could do something like this:
d.SpecialMethod = () => {
    return d.xyz;
};

Neha
July 28, 2016

# re: Creating a dynamic, extensible C# Expando Object

Hi,

Very nice post, very helpful. Have one question , In place of PropertyBag what else we can use (which is inbuilt). I don't want to create new class Property bag which create Xml serialization file.

Thanks

Perry
June 17, 2018

# re: Creating a dynamic, extensible C# Expando Object

Nice, this is pretty much what I was looking for. DO you have any recommendations on how an Expando object can be used as a ModelBinder in asp.net core 2.1? Ideally we would still use the built-in model mapping (no need to re-invent the wheel) and then just extend to do the dynamic mapping from the web form?


Rick Strahl
June 17, 2018

# re: Creating a dynamic, extensible C# Expando Object

@Perry - it will definitely work for the actual properties on the object. I'm not sure how MVC would work over the dynamic properties to retrieve values but if they are dynamic you probably wouldn't be interested in getting those bound any way and assign those manually most likely...


Perry
June 18, 2018

# re: Creating a dynamic, extensible C# Expando Object

Yup, I ended up creating an ActionFilter and overriding the OnActionExecuting method, which has already done the native mapping. I then add the custom dynamic field mapping to the model object. It's nice and clean and my Action function has the complete object mapped with the dynamic properties!


Rune Olsen
August 23, 2018

# re: Creating a dynamic, extensible C# Expando Object

Hi Rick,

Very nice and thorough post which hopefully helps me with the limitations of dynamics and the builtin ExpandoObject. I have a couple of problems though - and they occur both with your code shown in this post, as well as with the Westwind Nuget package:

When serializing I get an error about PropertyBag not being marked as serializable. When adding the tag to the class, serialization works. But then deserialization fails with an error about PropertyBag not having a constructor to deserialize the object.

Do you have any idea on whats wrong?


Rune Olsen
August 23, 2018

# re: Creating a dynamic, extensible C# Expando Object

Hi Again,

In my last comment I forgot to tell you that I'm using the builtin BinaryFormatter. I just read the comments on your original PropertyBag post, which also was about deserialization. I changed my code to use JSON.Net instead, I now I get another error: Member 'value' was not found.

Any idea?


Alberto Silva
February 15, 2019

# re: Creating a dynamic, extensible C# Expando Object

Great article, many thanks! I used your approach when submitting to Sap Business One on Hana via OData a known entity with custom user fields which are exposed as properties. With this approach, I just added the inheritance to your Expando object on the entity class I needed to submit and using the named indexed dictionary, set the value of the custom user fields, and the server processed it perfectly!


Dave Black
February 28, 2020

# re: Creating a dynamic, extensible C# Expando Object

Hi Rick,

I'm using Dynamics for working with a CMS (Storyblok). Each Content entry is defined as a "story". There are a few well-defined properties on each "story", but the rest are dynamic based on how the content editors define and create a "story".

{
  "story": {
    "id": 107350,
    "name": "My third post",
    "slug": "my-third-post",
    "content": {
      "component": "your_content_type",
      // and fields you define yourself are in here
    },
    ...

I have a simple POCO defined to use with JSON serialization to work with the values on the back-end:

    public class StoryblokDTO
    {
        public int id { get; set; }
        public string name { get; set; }
        public string slug { get; set; }
        public StoryblokContentDTO content { get; set; }
        //public dynamic content { get; set; }
    }

    public class StoryblokContentDTO : DynamicObject
    {
        public string component { get; set; }
        // elided for brevity

However, I have to cast the content property to dynamic anytime I access "dynamic properties at compile-time. Otherwise, I get compilation errors.

For example, the following does not work (compile errors):

    private static int SortResultsPrimary(StoryblokStoryDTO dto)
    {
        // 'search_priority' is a dynamic property
        // compile error on line below
        if (dto.content?.search_priority == null)
        {
            return 0;
        }

        // compile error on line below
        Int32.TryParse(dto.content.search_priority.ToString(), out int value);
        return value;
    }

But this works if I cast or explicitly treat the property as dynamic:

    private static int SortResultsPrimary(StoryblokStoryDTO dto)
    {
        dynamic content = dto?.content;
        // 'search_priority' is a dynamic property
        if (content?.search_priority == null)
        {
            return 0;
        }

        Int32.TryParse(content.search_priority.ToString(), out int value);
        return value;
    }

I'm surprised that the compiler doesn't seem to recognize that my instance is derived from DynamicObject and let me access the dynamic properties without casting or explicitly calling out the property as dynamic. Do you have any idea why this is?


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024