Home > DLR, WCF > WCF Serialization of DLR dynamic types

WCF Serialization of DLR dynamic types

I’m a huge fan of the DLR, as it provides terrific interoperability between C# and dynamic languages like IronPython. To create a C# class that works with the DLR, the easiest thing to do is derive from DynamicObject. One limitation arises when trying to use a dynamic type in a WCF service. Trying to use a DynamicObject-derived type will result in a runtime exception when trying to serialize with WCF’s DataContractSerializer. This class (which fails serialization) was my first attempt:

[DataContract]
public class SerializableDynamicObject : DynamicObject
{
    [DataMember]
    private Dictionary<string, object> props = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return props.TryGetValue(binder.Name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (props.ContainsKey(binder.Name))
            props[binder.Name] = value;
        else
            props.Add(binder.Name, value);
        return true;
    }
}

Trying to serialize an instance of SerializableDynamicObject results in the following exception:

Unhandled Exception: System.Runtime.Serialization.InvalidDataContractException:
Type ‘WCFDynamicObject.SerializableDynamicObject’ cannot inherit from a type that is not marked with DataContractAttribute or SerializableAttribute. Consider marking the base type ‘System.Dynamic.DynamicObject’ with DataContractAttribute or SerializableAttribute, or removing them from the derived type.

We can’t add attributes to DynamicObject, so we have to do this the *slightly* harder way by implementing IDynamicMetaObjectProvider rather than deriving from DynamicObject. The tricky part of this is creating the DynamicMetaObject which handles the evaluation of binding expressions. Luckily the DLR documentation on CodePlex has a great walkthrough for this.

The SerializableDynamicObject contains a dictionary of dynamic members and will serialize properly using WCF’s DataContractSerializer. Use it in place of DynamicObject when you want to be able to pass your dynamic types across WCF service boundaries.

[DataContract]
public class SerializableDynamicObject : IDynamicMetaObjectProvider
{
	[DataMember]
	private IDictionary<string,object> dynamicProperties = new Dictionary<string,object>();
	
	#region IDynamicMetaObjectProvider implementation
	public DynamicMetaObject GetMetaObject (Expression expression)
	{
		return new SerializableDynamicMetaObject(expression, 
			BindingRestrictions.GetInstanceRestriction(expression, this), this);
	}
	#endregion
	
	#region Helper methods for dynamic meta object support
	internal object setValue(string name, object value) 
	{
		dynamicProperties.Add(name, value);
		return value;
	}
	
	internal object getValue(string name) 
	{
		object value;
		if(!dynamicProperties.TryGetValue(name, out value)) {
			value = null;
		}
		return value;
	}
	
	internal IEnumerable<string> getDynamicMemberNames() 
	{
		return dynamicProperties.Keys;
	}
	#endregion
}


public class SerializableDynamicMetaObject : DynamicMetaObject
{
	Type objType;
	
	public SerializableDynamicMetaObject(Expression expression, BindingRestrictions restrictions, object value) 
		: base(expression, restrictions, value) 
	{
		objType = value.GetType();
	}
	
	public override DynamicMetaObject BindGetMember (GetMemberBinder binder)
	{
		var self = this.Expression;
		var dynObj = (SerializableDynamicObject)this.Value;
		var keyExpr = Expression.Constant(binder.Name);
		var getMethod = objType.GetMethod("getValue", BindingFlags.NonPublic | BindingFlags.Instance);
		var target = Expression.Call(Expression.Convert(self, objType),
		                             getMethod,
		                             keyExpr);
		return new DynamicMetaObject(target,
			BindingRestrictions.GetTypeRestriction(self, objType));
	}
	
	public override DynamicMetaObject BindSetMember (SetMemberBinder binder, DynamicMetaObject value)
	{
		var self = this.Expression;
		var keyExpr = Expression.Constant(binder.Name); 
		var valueExpr = Expression.Convert(value.Expression, typeof(object));
		var setMethod = objType.GetMethod("setValue", BindingFlags.NonPublic | BindingFlags.Instance);
		var target = Expression.Call(Expression.Convert(self, objType),
		setMethod, 
		keyExpr, 
		valueExpr);
		return new DynamicMetaObject(target,
			BindingRestrictions.GetTypeRestriction(self, objType));
	}
	
	public override IEnumerable<string> GetDynamicMemberNames ()
	{
		var dynObj = (SerializableDynamicObject)this.Value;
		return dynObj.getDynamicMemberNames();
	}
}

One warning, dynamic members can be anything, meaning at runtime someone could assign a method to one of these fields. If this is possible in your application, you’ll need to ensure any methods assigned to the dynamic type are not serialized. I’m leaving this as an exercise for the reader.

About these ads
Categories: DLR, WCF Tags: , ,
  1. January 19, 2011 at 5:15 pm | #1

    Great post! I’m trying to use your code, and running into an issue. I am getting a “The remote server returned an error: NotFound.” error when I try to access the service on the client. I’m curious to see an example of how you’re using this object to output data from WCF. In particular, I would like to see the OperationContract, as well as the implementation that adds the dynamic data to the response. I would really appreciate the help. Thanks!

    • loosexaml
      January 19, 2011 at 11:38 pm | #2

      I recommend having the SerializableDynamicObject data contract in a shared library that is referenced by both your client and your server projects. When you create the service reference, click “Advanced” and choose to reuse types in referenced assemblies so that you’ll use the dynamic object from the shared library. If you don’t do that, on the client you’ll end up with a generated type that just has a Dictionary for the dynamicProperties dictionary and doesn’t have the interface implementation.

      I threw together a sample and it is available here. One thing to note, my service method just takes any object, and I’ve added SerializableDynamicObject as a known type. That’s not required, but somewhat helpful if you want to keep your service very generic.

      I posted the sample service on one of my web apps if you want to take a look at the WSDL.

      If you’re still running into issues, post your code somewhere and I’ll take a look.

  2. Greivin Britton
    July 29, 2011 at 6:59 pm | #3

    Nice article, i have been looking for something like this, i have done something similar but using ISerializable instead, but now i having some issues with the generated client and now i’m going back to DataContract… but the incovenient with using DataContract and DynamicObject is that you can not add dynamic properties calling the service from javascript… (that’s why i started using ISerializable), but maybe you know a way to handle that…

  3. Adriaan
    October 13, 2011 at 8:35 am | #5

    You have just fried my brain… :) The possibilities…

    I’m taking it a bit further and have come into a scenario that gives errors, maybe you can comment. If I assign a List as an attribute to a SerializableDynamicObject I get a ‘channel closed’ error message in your console sample (code below), is this a limitation or am I doing something else wrong?

    Replace your service code with this to replicate error:
    dynamic d = new SerializableDynamicObject();
    d.Name = “SomeData”;

    d.Address = new SerializableDynamicObject();
    d.Address.Line1 = “123 Spring St.”;

    dynamic a = new SerializableDynamicObject();
    a.Items = new List(new object[] { d, d });
    return a;

    PS: Your client code has an error, it attempts to call a ‘client.GetData’ method where it should be ‘client.Process’

    • loosexaml
      October 13, 2011 at 8:53 am | #6

      WCF needs to know about the types being serialized, and this goes for collections, too, so you need to add List<Object> as a known type. Just add the attribute [KnownType(typeof(List<Object>))] to the SerializableDynamicObject class.

  4. Adriaan
    October 13, 2011 at 9:16 am | #7

    Thanks for the quick reply! That works like a charm!

  5. August 30, 2012 at 8:20 am | #8

    We’ve been trying to implement your approach and aren’t having any luck. Any chance you have a sample project that you’d be willing to share. Not sure what we’re wrong. Much appreciated if you’re willing/able.

    • loosexaml
      September 18, 2012 at 10:13 pm | #9

      My apologies for the late reply, but here is a sample, as provided in some earlier comments: http://www.ninjarobotarmy.com/ninjarobotarmy/dynsrv/dynsrv.zip

      Please let me know if you’re still experiencing issues, and if you can give specifics or post your code, I’d be glad to help.

      • Parag Gupta
        November 7, 2013 at 8:04 am | #10

        can you please uplad the zip, it is not available anymore?
        Thanks in advance.

  6. September 19, 2012 at 8:49 am | #11

    Thanks. I’ll give it try.

  7. Chris Cowdery
    September 19, 2012 at 11:18 am | #12

    Hi, I’m working with David Gerding trying to implement the dynamic serialization using the example you have here. However, we weren’t able to get the example to run correctly in the zip you linked. I’ve got 2 screen caps taken from the debugger which might be helpful.

    daisy.colum.edu\DynSerialCaps.zip

    I noticed in your example, the hosted service has only one method marked with OperationContract, named Process; but the in Main(), you call client.GetData(d). Is the interface used on the client side out of date or mismatched with the one used on the hosting service?

    Also, tried to see if I could add a service reference to DynSrv after bringing it up, in case this was the issue, however when i pointed it at the localhost address it hosted at (http://localhost:49390/) I got the error message:

    Metadata contains a reference that cannot be resolved: ‘http://localhost:49390/’.
    The remote server returned an unexpected response: (405) Method Not Allowed.
    The remote server returned an error: (405) Method Not Allowed.
    If the service is defined in the current solution, try building the solution and adding the service reference again.

    Appreciate your help,

    Chris C.

  8. Cyberchick
    September 27, 2012 at 10:53 am | #13

    I have tried the same, and for some reason when I recieve the object back after calling the following in my console app in my main function (using the wcf reference) i.e.

    static void Main(string[] args)
    {

    dynamic d = new SerializableDynamicObject();
    d.Testing = “this is a test.”;
    DynService.Service1Client client = new DynService.Service1Client();
    object res = client.Process(d);
    int i = 0;
    }

    the value returned comes back with no keys or values set to it.

  9. Martin
    October 27, 2012 at 11:02 am | #14

    What about ExpandoObject and converting it to/from serializable dictionary?
    http://theburningmonk.com/2011/05/idictionarystring-object-to-expandoobject-extension-method/

    • loosexaml
      October 27, 2012 at 10:07 pm | #15

      This would work if the ExpandoObject is a property of a DataContract so you can control the serialization and convert from ExpandoObject to Dictionary before serializing, convert from Dictionary to ExpandoObject after deserializing, and ensure the ExpandoObject will not be serialized and the Dictionary will be by setting DataMember attributes.

      However, if you’re trying to serialize an ExpandoObject (or derived) this way directly where the ExpandoObject *is* the DataContract instead of a subproperty, you will run into the same problem, I think, unless I’m misunderstanding your suggestion.

  10. David
    December 14, 2012 at 11:10 am | #16

    Hi, This is a nice solution, thanks for sharing.

    I am having a problem with it though, I can pass a dynamic back from my WCF Service but I can’t pass one up to it from the client.

    I guess this isn’t expected ?

    Thanks

    • loosexaml
      December 15, 2012 at 11:50 am | #17

      You should be able to, but it will require some work on the client end to serialize dynamic fields into the dynamic properties dictionary for sending to the server.

  11. Ganesh
    September 9, 2013 at 11:47 pm | #18

    Thanks for the great article but do you have a working sample for both:
    1) Sending dynamic data from Client
    2) Receiving dynamic data from WCF?

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: