DependencyProperty Serialization Part II

by Christoph Menge in .NET

First off, my apologies for not posting the second part earlier. I have had a lot to do in the past Months…

Part II of the article will show how a SerializationSurrogate works and explore wheter we can use it for generic de-serialization of DependencyProperties. Also, in this article I provide full source code for both parts of the article.

UPDATE: A much better version of this article can now be found at the CodeProject: DependencyProperty Serialization for Business Objects

Alternatively, directly get the source code here: http://www.emphess.net/wp-content/uploads/2009/03/bpboserialization1.zip

Well, let’s get started:

ISerializationSurrogate

This interface is designed as a stand-in for your class upon serialization and de-serialization.

It is quite simple and contains only two methods:

public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)

and

public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)

This is quite interesting, since there is an explanation on MSDN why they chose not to provide a SetObjectData() method for the ISerializable interface, so this makes quite a difference.

Surrogate Serialization

For serialization, we use almost the same code we used in part I, with a few modifications only:

public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
    PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(obj.GetType(),
            new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues |
                                                          PropertyFilterOptions.UnsetValues |
                                                          PropertyFilterOptions.Valid ) });

    List<FieldInfo> fieldInformation = ReflectionHelper.GetFieldInformation(obj.GetType());

    foreach (FieldInfo fiRover in fieldInformation)
    {
        object[] attribs = fiRover.GetCustomAttributes(typeof(NonSerializedAttribute), false);

        if (attribs.Length > 0)
            continue; // Don't serialize this

        info.AddValue(fiRover.Name, fiRover.GetValue(obj));
    }

    foreach (PropertyDescriptor propertyDescriptor in descriptors)
    {
        SerializeProperty(obj, info, propertyDescriptor);
    }
}

(Please note that I clean the code up a little, in the source download there is a lot of debugging output, etc.)

Surrogate De-Serialization

Now, it seems as if an implementation of ISerializationSurrogate might do the job and help us to circumvent the necessity to provide boring constructors through the SetObjectData() method. So let’s give it a try by basically using the deserialization code from part I:

public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
    Type thisType = obj.GetType();
    DependencyObject depObj = obj as DependencyObject;

    // De-Serialize Fields
    List<FieldInfo> fieldInformation = ReflectionHelper.GetFieldInformation(thisType);

    foreach (FieldInfo fiRover in fieldInformation)
    {
        object[] attribs = fiRover.GetCustomAttributes(typeof(NonSerializedAttribute), false);

        if (attribs.Length > 0)
            continue; // Don't deserialize this

        fiRover.SetValue(obj, info.GetValue(fiRover.Name, fiRover.FieldType));
    }

    // De-Serialize DependencyProperties
    PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(thisType,
            new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues |
                                                          PropertyFilterOptions.UnsetValues |
                                                          PropertyFilterOptions.Valid ) });

    // Try to use a mapping to set all DP values deferred. This will actually just
    // postpone the Exception
    List<DependencyValueMap> mappingList = new List<DependencyValueMap>();

    foreach (PropertyDescriptor propertyDescriptor in descriptors)
    {
        if (propertyDescriptor.Attributes[typeof(NonSerializedAttribute)] == null)
        {
            DependencyProperty dp = DependencyPropertyHelper.FindDependencyProperty(obj, propertyDescriptor.Name);

            if (null != dp)
            {
                mappingList.Add(new DependencyValueMap(dp, obj as DependencyObject, info.GetValue(propertyDescriptor.Name, propertyDescriptor.PropertyType)));
            }
            else
            {
                throw new SerializationException(String.Format(@"Failed to deserialize property '{0}' on object of type '{1}'.
Property could not be found. Version Conflict?", propertyDescriptor.Name, thisType));
            }
        }
    }

    foreach (DependencyValueMap dvRover in mappingList)
    {
        // This call will cause an InvalidOperationException:
        depObj.SetValue(dvRover.dp, dvRover.value);
    }

    return null;
}

Now, unfortunately, we cannot set the values of these DependencyProperties. The SetValue() method in DependencyObject will throw the oh-so-magic InvalidOperationException. I haven’t been able to find a way to go around this.

Summing it up

Well, that’s it basically. Where are we now?

  • It is possible to use DependencyProperties for business objects and serialize and deserialize them.
  • A reflection-based base class “SerializableDependencyProperty” does the job, as long as you stick to the naming convention and implement the deserialization constructor in all derived classes
  • Circumventing the explicit deserialization constructor seems impossible due to limitations of the DependencyObject base class

The Source

The sample solutions contains two projects:
WPFDependencyPropertySerialization shows how the serialization of part I can be accomplished.
WPFDependencyPropertySerializationError shows the extension using an ISerializationSurrogate and, as mentioned before, will lead to an Exception … which will not be caught so you can really feel it rumbling:
“Current local value enumeration is outdated because one or more local values have been set since its creation.”

You can download the source at http://www.emphess.net/wp-content/uploads/2009/03/bpboserialization1.zip

Have Fun!

A post scriptum on DependencyProperties for business entities

The project I’m currently working on comes with a very complex data structure. It consists of a set of trees which can be connected in various different ways. We also have to account for circular dependencies between these objects. The user can create and modify all these different types of connections.
Moreover, the architecture of the application is highly sophisticated and is based on Microsoft’s composite application guidance. A lot of modules interact with each other, each having their own data, similar to a ViewModel yet managed by a Presenter. What sounds a little messy has been proven to be the best solution in a number of discussions.
These business entities are based on DependencyProperties and a lot of crucial features depend on the functionality provided by the DependencyProperties. It works well, though I should mention that I did not use a number of DependencyProperty features in the first place. For example, coercion and validation coupled with automated de-serialization might lead into trouble, so keep this in mind should you ever intend to go down the path…

Post to Twitter Post to Delicious Post to Digg Post to Facebook

Related posts:

  1. DependencyProperty Serialization

Tags: , ,

← Previous

Next →

2 Comments

  1. [...] I just posted the source code of the Dependency Property Serialization sample, I realized it included a small piece of source code that might come in handy from time to [...]

  2. giorgos says:

    Your exception will happen if the DOs you call the SetValue on have not called the DO class constructor for some reason.

Leave a Comment