Saturday, February 26, 2011

Concepts of PropertyBag and the ExpandoObject

Need for PropertyBag
Lets consider the scenario of dynamic table creation in your database.User is allowed to create screens dynamically and for those screens they can even customize the business entities including creation of new entities.In this scenario you can’t create your business classes concretely.The reason is simple you don’t know what entities will come and how many properties they will have and their types.

The solution to this problem ie “Handling dynamic tables through business objects” is PropertyBag

What is PropertyBag
Property bag is nothing but a dictionary which keeps the property name and their values.Below is a sample implemenation.

public abstract class EntityBase
{
Dictionary<string, object> _propertyBag;
public object this[string propertyName]
{
get
{
return _propertyBag[propertyName];
}
set
{
_propertyBag[propertyName] = value;
}
}
}


Normally there will be one more checking for the typed properties.If the typed property exists the getter will return the value of the typed property and setter set the same.Dictionary will be used only when there is no typed property.

Using the property bag is through the indexer property.If there is a EntityBase derived Customer class with typed properties Name and Id, we can use this property bag to store a new value as Address property without recompiling the Customer class.


public class Customer : EntityBase
{
public int Id { get; set; }
public int Name { get; set; }
}




Customer cust = new Customer();
cust["Address"] = "Cochin";


Works perfect even though we can’t write like which we prefer.

cust.Address = "Cochin";


But this is type safe and from the view of typed language it should not compile.There should be no exemption for any class from having dynamic properties.


ExpandoObject

The above said story became history with the arrival of the ExpandoObject class in .Net 4.0 which allows dynamic properties.


dynamic  eo = new ExpandoObject();
eo.Joy = "My Name";


Joy is not at all a property of ExpandoObject class.But it compiles and works.The backend implementation is dictionary though.Ok there is one more argument that if we re-write our implementation using the new .net4 dynamic keyword will it work?

dynamic eo = new Customer ();
eo.Joy = "My Name";


Yes I meant the above code snippet only.This will compile.But on execution it will throw an exception of type RuntimeBinderException with the message “'Expression_Test.Customer' does not contain a definition for 'Joy'”.

Ok.Now you might be thinking that we can change the base class of our Customer entity class to ExpandoObject.But we can’t because the ExpandoObject is sealed :-(


DynamicObject class

There is another class named DynamicObject.We need to inherit from this class which implements the IDynamicMetaObjectProvider interface.Change the customer class as


public class Customer : DynamicObject
{
public int Id { get; set; }
public int Name { get; set; }
}


Compile the program.No errors…You may be thinking that its done.But actually not.When you run you will get the same RuntimeBinderException.Why we are getting this even after inheriting from a base class.That class should handle it right? Goto the definition of the DynamicObject class.You can see all the methods are virtual.Mean no code is written to handle the properties.ie property bag is not implemented there.Now what to do? Overriding in each and every entity class and implementing property bag is not possible because in a real business application there will be hundreds of business classes.

Return of the EntityBase class

Come back to EntityBase and inherit it from DynamicObject and override methods and use the same old dictionaryto set and get values.Here is the final code.


public abstract class EntityBase : DynamicObject
{
Dictionary<string, object> _propertyBag;
public object this[string propertyName]
{
get
{ //Code to check typed properties,if exists return that value
return _propertyBag[propertyName];
}
set
{ //Code to check typed properties,if exists set that property
_propertyBag[propertyName] = value;
}
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{ //Code to check typed properties,if exists return that value
return _propertyBag.TryGetValue(binder.Name, out result);
}

public override bool TrySetMember( SetMemberBinder binder, object value)
{ //Code to check typed properties,if exists set that property
_propertyBag[binder.Name] = value;
return true;
}
}
public class Customer : EntityBase

{
public int Id { get; set; }
public int Name { get; set; }
}


Now the below code will compile and run without any exceptions.

dynamic eo = new Customer   ();
eo.Joy = "My Name";


Unusual relation between Language and Framework SDK

All is well.Now we can write code normally for even non existing properties.How this magic is happening? The compiler doesn’t show any error message because it all done on a variable declared using dynamic keyword.Come to runtime.How the runtime knows that it should call the TryGetMember method? Some understandings between the framework types and the runtime?

Yes.The key is IDynamicMetaObjectProvider interface.If the class has implemented this interface the runtime will call the TryGetMember and accepts the value. Don’t you smell an unusual relation between runtime and the Microsoft classes? You must,if you had worked in any other languages or technologies.

No comments: