Loosing the Comparer when (De-)Serializing a Dictionary with the DataContractSerializer

I came across an interesting bug when working with a dictionary. I had a class using a dictionary as internal storage which should always ignore the casing of key when access the dictionary, Similar to the following code:

[Serializable]
public class TestContainer
{
    private readonly string id;
    private readonly Dictionary<string, string> dictionary;
 
    public TestContainer(string id)
    {
        this.id = id;
        this.dictionary = 
            new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    }
 
    public string GetValue(string key)
    {
        return this.dictionary[key];
    }
         
    public void AddValue(string key, string value)
    {
        this.dictionary.Add(key, value);
    }
}

There were unit tests checking all possible scenarios to make sure the values were always retrieved correctly (at least I thought so). The container was serialized and saved to a database by one application and deserialized by another application. The following sample has the same effect:

DataContractSerializer serializer =  new DataContractSerializer(typeof(TestContainer));
using (MemoryStream memoryStream = new MemoryStream())
{
    serializer.WriteObject(memoryStream, testcontainer);
    memoryStream.Position = 0;
 
    deserializedTestContainer = (TestContainer)serializer.ReadObject(memoryStream);
}

Everything seemed to work just fine, until I encountered a bug where a value in the dictionary couldn't be found. I looked at the serialized xml (which was quite a pain being several MB big) and debugged to find the missing value. When I found the value in the dictionary I was really surprised and couldn't explain it. Then I realized the casing of the key wasn't consistent and began to wonder whether I had missed a scenario in my unit tests. But everything seemed to be covered and all tests passed. While inspecting the dictionary object I discovered that the dictionary's comparer was a GenericEqualityComparer. That means the (de-)serialization ignores the comparer of a dictionary and resets it to the default. To fix the issue, the [OnDeserialized] attribute can be used:

[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
    this.dictionary = new Dictionary<string, string>(
        this.dictionary, StringComparer.OrdinalIgnoreCase);
}
comments powered by Disqus