locked
Object Deserialization Property Order RRS feed

  • Question

  • I have an object that implements INotifyPropertyChanged, and each of the properties in the control raise this event. The object also has an ObjectState property, that tracks if the object is deleted, added, modified, or "None" (ie straight out of the database).

    When I evaluate the property on the WCF side, the ObjectState is correctly set to none, but when I evaluate it on the client side, immediately after the return, the ObjectState is set to Modified.

    On debugging the code, it appears that the deserialization is setting the ObjectState to None, and then going to set several other properties on the object - which is changing it to modified.

    I'm guessing that this is solely due tot he order to property setting. Is there any way to define a property on a DataContract as being the last property to be set during deserialization? Or is there a better way to track object state? Below is example code:

    [DataContract]
        public abstract class ObjectBase : INotifyPropertyChanged
        {
            private ObjectChangeType _ObjectState;
            private string _Value;
    
    
            [DataMember]
            public ObjectChangeType ObjectState 
            {
                get { return _ObjectState; }
                set { _ObjectState = value; }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void NotifyPropertyChanged(String info)
            {
                if (ObjectState != ObjectChangeType.Added && ObjectState != ObjectChangeType.Deleted)
                {
                    ObjectState = ObjectChangeType.Modified;
                } 
    
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(info));
                }
            }
    
            public ObjectBase()
            {
                ObjectState = ObjectChangeType.Added;
            }
    
            [DataMember]
            public string Value
            {
                get { return _Value; }
                set
                {
                    if (value != _Value)
                    {
                        _Value = value;
                        NotifyPropertyChanged("Value");
                    }
                }
            }
    
        }
    

     

    Tuesday, December 20, 2011 4:35 PM

Answers

  • You can use the Order property of the [DataMember] attribute to do that - see an example in the code below.

        public class Post_409e2909_4ba0_436c_aa28_7099d6ccbd0d
        {
            public enum ObjectChangeType { Added, Deleted, Modified, None }
            [DataContract]
            public abstract class ObjectBase : INotifyPropertyChanged
            {
                private ObjectChangeType _ObjectState;
                private string _Value;
    
                [DataMember(Order = 100)]
                public ObjectChangeType ObjectState
                {
                    get { return _ObjectState; }
                    set { _ObjectState = value; }
                }
    
                public event PropertyChangedEventHandler PropertyChanged;
    
                protected void NotifyPropertyChanged(String info)
                {
                    if (ObjectState != ObjectChangeType.Added && ObjectState != ObjectChangeType.Deleted)
                    {
                        ObjectState = ObjectChangeType.Modified;
                    }
    
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(info));
                    }
                }
    
                public ObjectBase()
                {
                    ObjectState = ObjectChangeType.Added;
                }
    
                [DataMember(Order = 1)]
                public string Value
                {
                    get { return _Value; }
                    set
                    {
                        if (value != _Value)
                        {
                            _Value = value;
                            NotifyPropertyChanged("Value");
                        }
                    }
                }
            }
    
            public class ObjectDerived : ObjectBase { }
    
            public static void Test()
            {
                MemoryStream ms = new MemoryStream();
                ObjectDerived od = new ObjectDerived { Value = "Hello", ObjectState = ObjectChangeType.None };
                DataContractSerializer dcs = new DataContractSerializer(typeof(ObjectDerived));
                dcs.WriteObject(ms, od);
                Console.WriteLine("Serialized: {0}", Encoding.UTF8.GetString(ms.ToArray()));
                ms.Position = 0;
                ObjectDerived od2 = (ObjectDerived)dcs.ReadObject(ms);
                Console.WriteLine("State: {0}", od2.ObjectState);
            }
        }
    
    

     


    Carlos Figueira
    Tuesday, December 20, 2011 7:02 PM
  • Ok, that complicates a little the problem, but it still can be solved. Basically, the Order property won't help you here, because the members of the base class are always deserialized before the members of the derived class. You're on the right track here. You can create a private variable which can be used to indicate when the deserialization episode is happening. That variable is set when the deserialization is starting (on the [OnDeserializing]) callback, then reset when it ends (on [OnDeserialized]). Then the NotifyPropertyChanged can ignore events which happen during that time. The code below shows this implementation, and the post at http://blogs.msdn.com/b/carlosfigueira/archive/2011/09/06/wcf-extensibility-serialization-callbacks.aspx has more information on those serialization callbacks.

     

        public class Post_409e2909_4ba0_436c_aa28_7099d6ccbd0d
        {
            public enum ObjectChangeType { Added, Deleted, Modified, None }
    
            [DataContract]
            public abstract class ObjectBase : INotifyPropertyChanged
            {
                private ObjectChangeType _ObjectState;
    
                private bool isDeserializing;
    
                [DataMember]
                public ObjectChangeType ObjectState
                {
                    get { return _ObjectState; }
                    set
                    {
                        _ObjectState = value;
                    }
                }
    
                [OnDeserializing]
                void OnDeserializing(StreamingContext context)
                {
                    this.isDeserializing = true;
                }
    
                [OnDeserialized]
                void OnDeserialized(StreamingContext context)
                {
                    this.isDeserializing = false;
                }
    
                public event PropertyChangedEventHandler PropertyChanged;
    
                protected void NotifyPropertyChanged(String info)
                {
                    if (!this.isDeserializing)
                    {
                        if (ObjectState != ObjectChangeType.Added && ObjectState != ObjectChangeType.Deleted)
                        {
                            ObjectState = ObjectChangeType.Modified;
                        }
    
                        if (PropertyChanged != null)
                        {
                            PropertyChanged(this, new PropertyChangedEventArgs(info));
                        }
                    }
                }
    
                public ObjectBase()
                {
                    ObjectState = ObjectChangeType.Added;
                }
            }
    
            [DataContract]
            public class ObjectDerived : ObjectBase
            {
                private string _Value;
    
                [DataMember]
                public string Value
                {
                    get { return _Value; }
                    set
                    {
                        if (value != _Value)
                        {
                            _Value = value;
                            NotifyPropertyChanged("Value");
                        }
                    }
                }
            }
    
            public static void Test()
            {
                MemoryStream ms = new MemoryStream();
                ObjectDerived od = new ObjectDerived { Value = "Hello", ObjectState = ObjectChangeType.None };
                DataContractSerializer dcs = new DataContractSerializer(typeof(ObjectDerived));
                dcs.WriteObject(ms, od);
                Console.WriteLine("Serialized: {0}", Encoding.UTF8.GetString(ms.ToArray()));
                ms.Position = 0;
                ObjectDerived od2 = (ObjectDerived)dcs.ReadObject(ms);
                Console.WriteLine("State: {0}", od2.ObjectState);
            }
        }
    
    

     


    Carlos Figueira
    Wednesday, December 21, 2011 10:06 AM

All replies

  • You can use the Order property of the [DataMember] attribute to do that - see an example in the code below.

        public class Post_409e2909_4ba0_436c_aa28_7099d6ccbd0d
        {
            public enum ObjectChangeType { Added, Deleted, Modified, None }
            [DataContract]
            public abstract class ObjectBase : INotifyPropertyChanged
            {
                private ObjectChangeType _ObjectState;
                private string _Value;
    
                [DataMember(Order = 100)]
                public ObjectChangeType ObjectState
                {
                    get { return _ObjectState; }
                    set { _ObjectState = value; }
                }
    
                public event PropertyChangedEventHandler PropertyChanged;
    
                protected void NotifyPropertyChanged(String info)
                {
                    if (ObjectState != ObjectChangeType.Added && ObjectState != ObjectChangeType.Deleted)
                    {
                        ObjectState = ObjectChangeType.Modified;
                    }
    
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(info));
                    }
                }
    
                public ObjectBase()
                {
                    ObjectState = ObjectChangeType.Added;
                }
    
                [DataMember(Order = 1)]
                public string Value
                {
                    get { return _Value; }
                    set
                    {
                        if (value != _Value)
                        {
                            _Value = value;
                            NotifyPropertyChanged("Value");
                        }
                    }
                }
            }
    
            public class ObjectDerived : ObjectBase { }
    
            public static void Test()
            {
                MemoryStream ms = new MemoryStream();
                ObjectDerived od = new ObjectDerived { Value = "Hello", ObjectState = ObjectChangeType.None };
                DataContractSerializer dcs = new DataContractSerializer(typeof(ObjectDerived));
                dcs.WriteObject(ms, od);
                Console.WriteLine("Serialized: {0}", Encoding.UTF8.GetString(ms.ToArray()));
                ms.Position = 0;
                ObjectDerived od2 = (ObjectDerived)dcs.ReadObject(ms);
                Console.WriteLine("State: {0}", od2.ObjectState);
            }
        }
    
    

     


    Carlos Figueira
    Tuesday, December 20, 2011 7:02 PM
  • Ah sorry, my bad. Your example would work brilliantly for the example I provided, and is something I wasn't really aware of.

    However, for simplicity, I included the Value property in ObjectBase. In reality that would be a property on a derived class, and I need the ObjectState to be the very last property to be set. Reading up on the Order property, it appears that that only controls order within the class itself, and derived class properties always serialize after base class properties.

    This website (http://jeffbarnes.net/blog/post/2007/05/08/WCF-Serialization-Order-in-Data-Contracts.aspx) hints at the end, that you can customize the Serialization, but I cant see any posts that cover this on his blog.

    Alternatively, I see some mention of the [OnDeserializing] attribute. Could I use that to either disable the NotifyPropertyChanged method (I guess I'd have to create a bool variable that allowed NotifyPropertyChanged to know it was being Deserialized and not to change the ObjectState), or store the original ObjectState value for resetting in the [OnDeserialized] method?

    Wednesday, December 21, 2011 8:29 AM
  • Ok, that complicates a little the problem, but it still can be solved. Basically, the Order property won't help you here, because the members of the base class are always deserialized before the members of the derived class. You're on the right track here. You can create a private variable which can be used to indicate when the deserialization episode is happening. That variable is set when the deserialization is starting (on the [OnDeserializing]) callback, then reset when it ends (on [OnDeserialized]). Then the NotifyPropertyChanged can ignore events which happen during that time. The code below shows this implementation, and the post at http://blogs.msdn.com/b/carlosfigueira/archive/2011/09/06/wcf-extensibility-serialization-callbacks.aspx has more information on those serialization callbacks.

     

        public class Post_409e2909_4ba0_436c_aa28_7099d6ccbd0d
        {
            public enum ObjectChangeType { Added, Deleted, Modified, None }
    
            [DataContract]
            public abstract class ObjectBase : INotifyPropertyChanged
            {
                private ObjectChangeType _ObjectState;
    
                private bool isDeserializing;
    
                [DataMember]
                public ObjectChangeType ObjectState
                {
                    get { return _ObjectState; }
                    set
                    {
                        _ObjectState = value;
                    }
                }
    
                [OnDeserializing]
                void OnDeserializing(StreamingContext context)
                {
                    this.isDeserializing = true;
                }
    
                [OnDeserialized]
                void OnDeserialized(StreamingContext context)
                {
                    this.isDeserializing = false;
                }
    
                public event PropertyChangedEventHandler PropertyChanged;
    
                protected void NotifyPropertyChanged(String info)
                {
                    if (!this.isDeserializing)
                    {
                        if (ObjectState != ObjectChangeType.Added && ObjectState != ObjectChangeType.Deleted)
                        {
                            ObjectState = ObjectChangeType.Modified;
                        }
    
                        if (PropertyChanged != null)
                        {
                            PropertyChanged(this, new PropertyChangedEventArgs(info));
                        }
                    }
                }
    
                public ObjectBase()
                {
                    ObjectState = ObjectChangeType.Added;
                }
            }
    
            [DataContract]
            public class ObjectDerived : ObjectBase
            {
                private string _Value;
    
                [DataMember]
                public string Value
                {
                    get { return _Value; }
                    set
                    {
                        if (value != _Value)
                        {
                            _Value = value;
                            NotifyPropertyChanged("Value");
                        }
                    }
                }
            }
    
            public static void Test()
            {
                MemoryStream ms = new MemoryStream();
                ObjectDerived od = new ObjectDerived { Value = "Hello", ObjectState = ObjectChangeType.None };
                DataContractSerializer dcs = new DataContractSerializer(typeof(ObjectDerived));
                dcs.WriteObject(ms, od);
                Console.WriteLine("Serialized: {0}", Encoding.UTF8.GetString(ms.ToArray()));
                ms.Position = 0;
                ObjectDerived od2 = (ObjectDerived)dcs.ReadObject(ms);
                Console.WriteLine("State: {0}", od2.ObjectState);
            }
        }
    
    

     


    Carlos Figueira
    Wednesday, December 21, 2011 10:06 AM
  • Perfect. Thats exactly what I was thinking, and appears to work a treat.

    Thanks for the help!

    Wednesday, December 21, 2011 1:36 PM
  • Carlos, you said:

    Basically, the Order property won't help you here, because the members of the base class are always deserialized before the members of the base class.

     

    Did you mean the base class members deserialize before the child class members?

    Wednesday, January 4, 2012 4:31 PM
  • Oops, you're correct. The members of the base class are always deserialized before the members of the derived class. I'll edit that post with the correction, thanks.
    Carlos Figueira
    Wednesday, January 4, 2012 5:05 PM