.NET, Architecture, ALM, Cloud

Publicité

Dynamic Object & WCF DataContract

 

Considérons le besoin suivant : Nous voulons créer un objet UserInfos devant transiter par un service WCF permettant au client d'ajouter des propriétés sur un utilisateur (site web, icq, age...) et côté serveur, le code se charge de récupérer ces propriétés pour les stocker en xml en base.

 

Suivant les besoins des différents clients, les propriétés d'un utilisateur sont divers et variés et doivent pouvoir être étendu sans modifier le contrat de communication (et donc re-livrer le service, proxy client...).

 

Nous allons donc utiliser un objet dynamique UserInfos pour permettre au client à partir de son objet de définir les propriétés propre à ses besoins :

 

    [DataContract]

    public class UserInfos : DynamicObject

    {

        [DataMember]

        private Dictionary<string, object> _infos = new Dictionary<string, object>();

 

        public override bool TryGetMember(GetMemberBinder binder, out object result)

        {

            return _infos.TryGetValue(binder.Name, out result);

        }

 

        public override bool TrySetMember(SetMemberBinder binder, object value)

        {

            if (_infos.ContainsKey(binder.Name))

                _infos[binder.Name] = value;

            else

                _infos.Add(binder.Name, value);

            return true;

        }

    }

 

Problème ! : Nous nous rendons compte que le Serializer WCF DataContractSerializer ne gère pas les DynamicObject en obtenant l'erreur suivante :

 

Type 'UserInfos' 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.

 

Heureusement la DLR a toujours une solution à nous apporter lorsqu'on croit que nous sommes perdu !

La solution consiste à créer un provider pour gérer son objet dynamique en implémentant l'interface " IDynamicMetaObjectProvider" et en créant son objet dynamique en utilisant la classe de base "DynamicMetaObject" pour gérer l'accès et définition des propriétés utilisateurs :

 

 

    [DataContract]

    public class UserInfos : IDynamicMetaObjectProvider

    {

        [DataMember]

        private IDictionary<string, object> _props = new Dictionary<string, object>();

 

        #region IDynamicMetaObjectProvider implementation

        public DynamicMetaObject GetMetaObject(Expression expression)

        {

            return new UserInfosMetaObject(expression,

                BindingRestrictions.GetInstanceRestriction(expression, this), this);

        }

        #endregion

 

        internal object SetValue(string name, object value)

        {

            _props.Add(name, value);

            return value;

        }

 

        internal object GetValue(string name)

        {

            object value;

            if (!_props.TryGetValue(name, out value))

            {

                value = null;

            }

            return value;

        }

 

        internal IEnumerable<string> GetDynamicMemberNames()

        {

            return _props.Keys;

        }

 

    }

 

   

 

public class UserInfosMetaObject : DynamicMetaObject

    {

        Type objType;

 

        public UserInfosMetaObject(Expression expression, BindingRestrictions restrictions, object value)

            : base(expression, restrictions, value)

        {

            objType = value.GetType();

        }

 

        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)

        {

            //On appel la méthode "GetValue" de notre objet "UserInfos"

            var target = Expression.Call(Expression.Convert(this.Expression, objType),

                                         objType.GetMethod("GetValue", BindingFlags.NonPublic | BindingFlags.Instance),

                                         Expression.Constant(binder.Name));

            return new DynamicMetaObject(target,

                BindingRestrictions.GetTypeRestriction(this.Expression, objType));

        }

 

        public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)

        {

            //On appel la méthode "SetValue" de notre objet "UserInfos"

            var target = Expression.Call(Expression.Convert(this.Expression, objType),

                 objType.GetMethod("SetValue", BindingFlags.NonPublic | BindingFlags.Instance),

                 Expression.Constant(binder.Name),

                 Expression.Convert(value.Expression, typeof(object)));

 

            return new DynamicMetaObject(target,

                BindingRestrictions.GetTypeRestriction(this.Expression, objType));

        }

 

        public override IEnumerable<string> GetDynamicMemberNames()

        {

            //Récupération des membres de l'objet dynamique

            var dynObj = (UserInfos)this.Value;

            return dynObj.GetDynamicMemberNames();

        }

    }

 

Faisons à présent le test de sérialisation :

 

dynamic userInfos = new UserInfos();

            userInfos.WebSite = "http://www.mysite.com";

            userInfos.ICQ = "124587141";

            userInfos.Age = 22;

            StringBuilder xml = new StringBuilder();

            using (XmlWriter xmlWriter = XmlWriter.Create(xml))

            {

                DataContractSerializer ser = new DataContractSerializer(typeof(UserInfos));

                ser.WriteObject(

                    xmlWriter, userInfos);

            }

            Console.WriteLine(xml.ToString());

 

 

  Et nous obtenons bien notre xml :

 

  <?xml version="1.0" encoding="utf-16" ?>

- <UserInfos xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication3">

- <_props xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">

- <d2p1:KeyValueOfstringanyType>

  <d2p1:Key>WebSite</d2p1:Key>

  <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:string">http://www.mysite.com</d2p1:Value>

  </d2p1:KeyValueOfstringanyType>

- <d2p1:KeyValueOfstringanyType>

  <d2p1:Key>ICQ</d2p1:Key>

  <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:string">124587141</d2p1:Value>

  </d2p1:KeyValueOfstringanyType>

- <d2p1:KeyValueOfstringanyType>

  <d2p1:Key>Age</d2p1:Key>

  <d2p1:Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:int">22</d2p1:Value>

  </d2p1:KeyValueOfstringanyType>

  </_props>

  </UserInfos>

 

 

Publicité
Retour à l'accueil
Partager cet article
Repost0
Pour être informé des derniers articles, inscrivez vous :
Commenter cet article