Customizing Serialization of Entities in the ADO.NET Data Services Client Library

Customizing Serialization of Entities in the ADO.NET Data Services Client Library

Rate This
  • Comments 13

This post is inspired by this forum thread :Data services client -- exception on saveChanges

Problem Statement


I have added some custom proeprties to the Entity Types Generated by DataSvcUtil.exe /Add Service Reference / Hand Coded ,
these properties do not exist on the server and should not be sent to the server .
When I try to save an object of a Type which has custom client-Side only properties to the Data Service using AddObject and SaveChanges,
I get the following error :

Error processing request stream. The property name '{PropertyName}' specified for type '{TypeName} is not valid

This error is basically from the server telling the client library that its sending a payload which does not seem to match the definition
of the Entity Type on the server.

We can solve this problem by customizing the Payload that the client generates when it sends the entity to the server.
The DataServiceContext exposes an event called WritingEntity that is fired right before we send the payload over the wire.

Its argumentlist contains ReadingWritingEntityEventArgs which gives you access to the Entity Instance being Serialized ( e.Entity ) 
and the ATOM Payload (e.Data ) we are about to send to the Server.

Removing a property from the ATOM Payload that is being sent to the server.
void dataContext_WritingEntity(object sender, ReadingWritingEntityEventArgs e) {

    // e.Data gives you the XElement for the Serialization of the Entity 
    //Using XLinq  , you can  add/Remove properties to the element Payload  
    XName xnEntityProperties = XName.Get("properties", e.Data.GetNamespaceOfPrefix("m").NamespaceName);
    XElement xePayload = e.Data.Descendants().Where<XElement>(xe => xe.Name == xnEntityProperties).First<XElement>();
   //The XName of the property we are going to remove from the payload
    XName xnProperty = XName.Get(“{PropertyName}”, e.Data.GetNamespaceOfPrefix("d").NamespaceName);

    //Get the Property of the entity  you don't want sent to the server
    XElement xeRemoveThisProperty = xePayload.Descendants().Where<XElement>(xe => xe.Name == xnProperty).First<XElement>();
    //Remove this property from the Payload sent to the server 
     xeRemoveThisProperty.Remove();

     }
}

Generalizing this further , we want to make it easier to customize the serialization of multiple entity types without having to write
multiple copies of the WritingEntity event handler.
We will add an attribute that specfies that a certain CLR property should not be serialized in the ATOM Payload when the client library
sends the entity to the server.

    /// <summary>
    /// Properties marked with this Attribute are not serialized in the payload when sent to the server
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class DoNotSerializeAttribute : Attribute
    {
    }

Now , we change the WritingEntity Event handler to remove any property  that has this attribute on it .

        void DataServiceContextEx_WritingEntity(object sender, ReadingWritingEntityEventArgs e)
        {
            // e.Data gives you the XElement for the Serialization of the Entity 
            //Using XLinq  , you can  add/Remove properties to the element Payload  
            XName xnEntityProperties = XName.Get("properties", e.Data.GetNamespaceOfPrefix("m").NamespaceName);
            XElement xePayload = null;
            foreach (PropertyInfo property in e.Entity.GetType().GetProperties())
            {
                object[] doNotSerializeAttributes = property.GetCustomAttributes(typeof(DoNotSerializeAttribute), false);
                if (doNotSerializeAttributes.Length > 0)
                {
                    if (xePayload == null)
                    {
                        xePayload = e.Data.Descendants().Where<XElement>(xe => xe.Name == xnEntityProperties).First<XElement>();
                    }
                    //The XName of the property we are going to remove from the payload
                    XName xnProperty = XName.Get(property.Name, e.Data.GetNamespaceOfPrefix("d").NamespaceName);
                    //Get the Property of the entity  you don't want sent to the server
                    XElement xeRemoveThisProperty = xePayload.Descendants().Where<XElement>(xe => xe.Name == xnProperty).First<XElement>();
                    //Remove this property from the Payload sent to the server 
                    xeRemoveThisProperty.Remove();
                }
            }
        }

We can encapsulate this functionality into its own class and any DataServiceContext type that inherits from this class should be able to
inherit this functionality too .
Complete Source Code :

Using this in your applications :

Create a partial class with the same name as shown above and include it in your application to get this behavior.

Attribute your types with the DoNotSerializeAttribute  attribute so that this property is not serialized.

 public class TestType
    {
        public int ID { get; set; }
        /// <summary>
        /// This Property is client-only , should not be sent to the server
        /// </summary>
        [DoNotSerialize]
        public string ClientOnlyProperty { get; set; }
    }
Leave a Comment
  • Please add 6 and 5 and type the answer here:
  • Post
  • One of the most common feature requests we hear for Astoria is the ability to mark properties on an entity

  • You know this completes the circle since whe can partialize a data entity to implemente INotifyPropertyChanged and we can mark an attribute to dont be part of the context we now really can customize and use a data entity in business layer so we dont need extra classes to achieve this, thats one of the many things i love data services, great post! thank you!!

  • This is a very cool idea, but I'm curious if it can be used on a field in an ADO.NET entity model?  If so, how would I set a field there to have the DoNotSerialize property?

  • Hi Charles,

    This property is client-side only.  

    By design , the server does not expose any events for us to hook in and change the markup .

    Which scenario are you trying to solve with this ?

    -Phani

  • I may be missing something but I'm trying to use this in a Silverlight project and my derived class that inherits from DataServiceContext is auto generated for my Service Reference.  If I update the code that defines my Service Reference to inherit from my new custom class it is simply blown away the next time I refresh the service.  

    What am I missing?

  • Hi John,

    You are right . I wanted to update this blog entry to use a partial class instead of an inherited class for a long time .

    Using a partial class preserves this code in another code file and doesnt get affected if you re-generate the client proxy classes.

    -Phani

  • I was having the same issue with ADO.NET Service generated classes that we wanted to extend (since they are partial classes).

    I tried several techniques to do the serialization in order to exclude the newly created (extended) properties to the ADO.NET generated classes, since ADO.NET detects the newly created properties; which causes an exception specifying the NONE existing properties.

    Some didn't work, other were very tricky. So I found this solution much cleaner and practical.

    The only problem I found was the LINQ code to access the extended properties to remove them from the ATOM Payload, doesn't work anymore. The methods to process the Lambda expression don't exist or the version I have in silverlight is different. So I fix it this way to eliminate a propertyname (string):

    XName xNameProperty = XName.Get(propertyName, rootElement.GetNamespaceOfPrefix("d").NamespaceName);

    //Get descendants for xNameProperty object within ALL xElements

    IEnumerable<XElement> PropertyElements = rootElement.Descendants(xNameProperty);

    //Initialize variable

    XElement elementxHasProperty = null;

    //Find xElement for the searched propertyName

    foreach (XElement element in PropertyElements)

    {

           //Verify if it is the property looked for

           if (element.Name.LocalName == xNameProperty.LocalName)

           {

               elementxHasProperty = element;

               break;

            }

    }

    //Verify if the propertyName was found to remove the property from being sent to ADO.NET Services

    if (elementxHasProperty != null)

                   elementxHasProperty.Remove();

    Thank you so much for your guidance

  • Hi,

    We had a tough time trying to solve this issue of not sending the custom properties of the entities to the server. your article was of great help :). Thanks a lot

  • This post inspired me to write an [Encrypt] attribute to encrypt the contents of each table property. Source code: azuretableencrypt.codeplex.com

    I'm tracking issues on this MSDN thread.

    social.msdn.microsoft.com/.../5ba62aa0-46c1-4599-b44f-8df4798cacad

  • When ADO tried to write one of my properties (it was a 'get' only) it crashed the original Phani's code. After slight change all came to normal and worked. Great job Phani! Thanks a lot!

    XElement xeRemoveThisProperty = xePayload

                                              .Descendants()

                                              .Where<XElement>(xe => xe.Name == xnProperty)

                                              .FirstOrDefault<XElement>();

                               //Remove this property from the Payload sent to the server

                               if (xeRemoveThisProperty != null)

                                   xeRemoveThisProperty.Remove();

  • Hi Phani,

       I describe some goals to my table storage entity class in stackoverflow.com/.../effective-custom-azure-table-storage-entity-serialization, two of them I think that can't accomplish with this approach. There are another approach?!

    Thank's

  • AHHH what a great solution. Too bad I found this right at the end of my project! There was some bad code written because of the need for client-side metadata that I thought couldn't be placed in the entity.

    It also allows helps solve a problem of controls such as the telerik RadGridView wanting to sort and group data based on the binding source and not the converted value. Sometimes it can't sort and group on the binding source. I am thinking about storing the converted value in one of these extended properties.

  • This seems to work great when writing entities to local (development) storage. However, the schema still seems to contain the "non-serialized" properties. When trying to load an entity from storage that has a read-only property that should be ignored, an exception is thrown that claims the property has no setter.

    I don't know enough about the differences between local storage and cloud storage, and I can't find where the schema is stored or how to prevent the ignored properties from being added to the schema. TableServiceContext.IgnoreMissingProperties seems to have no effect.

    I've considered modifying the ReadingEntity event to ignore these properties, but this seems like a hack. The stored schema should not contain the properties in the first place.

    Any ideas?

Page 1 of 1 (13 items)