Bidirectional Graph deserialization failure

Topics: bug, serialization
Mar 12, 2010 at 5:44 PM

Consider the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Runtime.Serialization;
using System.IO;

using QuickGraph;
using QuickGraph.Algorithms;

namespace SerializationCheck
{
    class Program
    {
        static void Main(string[] args)
        {
            GraphNode node = PopulateNode();

            string data = SerializeNode(node);

            Console.WriteLine("Node " + node.NodeName + " serialized as : " + data);

            GraphNode deserialized = DeserializeNode<GraphNode>(data);

            bool equals = deserialized.Equals(deserialized);
        }

        public static GraphNode PopulateNode()
        {
            var parentA = new GraphNode("ParentA", Guid.NewGuid());
            var childA1 = new GraphNode("ChildA1", Guid.NewGuid());
            var childA2 = new GraphNode("ChildA2", Guid.NewGuid());

            parentA.AddVerticesAndEdge(new SEdge<GraphNode>(parentA, childA1));
            parentA.AddVerticesAndEdge(new SEdge<GraphNode>(parentA, childA2));

            return parentA;
        }

        public static string SerializeNode(GraphNode obj)
        {
            try
            {
                DataContractSerializer dataContractSerializer = new DataContractSerializer(obj.GetType());
                String text;
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    dataContractSerializer.WriteObject(memoryStream, obj);

                    byte[] data = new byte[memoryStream.Length];

                    Array.Copy(memoryStream.GetBuffer(), data, data.Length);

                    text = Encoding.UTF8.GetString(data);
                }
                return text;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public static T DeserializeNode<T>(string xml)
        {
            try
            {
                DataContractSerializer dataContractSerializer = new DataContractSerializer(typeof(GraphNode));

                UTF8Encoding enc = new UTF8Encoding();
                byte[] data = enc.GetBytes(xml);

                MemoryStream ms = new MemoryStream();
                ms.Write(data, 0, data.Length);

                ms.Position = 0;

                object deserialized = dataContractSerializer.ReadObject(ms);
                T obj = (T)deserialized;
                return obj;
            }
            catch (Exception)
            {
                throw;
            }
        }
    }


    [DataContract(IsReference = true)]
    public class GraphNode
    {
        [DataMember]
        private BidirectionalGraph<GraphNode, SEdge<GraphNode>> _dependencyTree;

        private bool _deserializing;

        [DataMember]
        public string NodeName { get; set; }

        [DataMember]
        public Guid NodeID { get; set; }

        public override int GetHashCode()
        {
            if (_deserializing)
            {
                Console.WriteLine("Failed...");
            }
            System.Diagnostics.Debug.Assert(_deserializing == false && !Guid.Equals(Guid.Empty, NodeID), "Object hasn't been fully deserialized yet - invalid state");
            return NodeID.GetHashCode();
        }

        public GraphNode(string name, Guid id)
        {
            // WCF serialization won't call constructor, so perform construction in the deserialization event handler
            OnDeserialized(new StreamingContext());

            NodeName = name;
            NodeID = id;
        }

        public void AddVerticesAndEdge(SEdge<GraphNode> edge)
        {
            _dependencyTree.AddVerticesAndEdge(edge);
        }

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            if (_dependencyTree == null)
            {
                _dependencyTree = new BidirectionalGraph<GraphNode, SEdge<GraphNode>>();
            }

            Console.WriteLine("Completed Deserialization of Node " + NodeName);
            _deserializing = false;
        }

        [OnSerialized]
        private void OnSerialized(StreamingContext context)
        {
            Console.WriteLine("Completed Serialization of Node " + NodeName);
        }

        [OnDeserializing]
        private void OnDeserializing(StreamingContext context)
        {
            Console.WriteLine("Started Deserialization of Node " + NodeName);
            _deserializing = true;
        }

        [OnSerializing]
        private void OnSerializing(StreamingContext context)
        {
            Console.WriteLine("Started Serialization of Node " + NodeName);
        }
    }
}

The serialization performs as expected, however deserialization calls GetHashCode() prior to the object having been completely de-serialized. My first guess is because the VertexEdgeDictionary inherits from Dictionary<>. 
Is this a bug? If not, what is an appropriate work around which preserves the potential self-referencing nature of the GraphNode?