Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic ExpandoObject in WCF

Tags:

c#

dynamic

wcf

Trying to Ping / Pong my service with an operation of enum type process and dynamic data.

[ServiceContract ( CallbackContract = typeof ( iStackoverflowCallBack ) )]
public interface iStackoverflow
{
    [OperationContract]
    void Ping ( Process Operation , dynamic Data );
}

[ServiceContract ( )]
public interface iStackoverflowCallBack
{
    [OperationContract]
    void Pong ( Process Operation , dynamic Data );
}

Why this service having issues connecting ?

  • When implementing both interfaces dynamic converted automatically to object.
  • When pinging my service from consumer, the ping doesn't reach the service at all but the service is working properly.

Solution :

[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();
    }
}
like image 898
Ahmed Ghoneim Avatar asked Nov 03 '22 20:11

Ahmed Ghoneim


2 Answers

The arguments for any WCF Operation Contract must be WCF Data Contract (or primitive types) or else the data will not be serialized and transferred.

http://msdn.microsoft.com/en-us/library/ms733127.aspx

like image 86
Glenn Ferrie Avatar answered Nov 13 '22 17:11

Glenn Ferrie


To expand on the solution from Ahmed on how to use it.

The answer is taken from this page: https://loosexaml.wordpress.com/2011/01/01/wcf-serialization-of-dlr-dynamic-types/

[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();
    }
}

A simple way to use it:

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;

However keep in mind that WCF requires knowledge of data types, since it most most basic data types like string, int etc it isn't a problem. But if you're using a custom data type in your dynamic object, you must define in using the [KnownType(typeof(XXX))] directive. This applies to enums, List<> and any other custom classes.

For example:

[KnownType(typeof(MyCustomEnum))]
[KnownType(typeof(List<object>))]
[DataContract]
public class SerializableDynamicObject : IDynamicMetaObjectProvider
...
like image 20
rboy Avatar answered Nov 13 '22 19:11

rboy