I have been running into a situation a lot lately, especially with applications that utilize WCF services.  Specifically, it deals with deserialization, so I wanted to write up a little bit about what I have found and some best practices.

Problem

Consider the following scenario.  You have the following object, defined in your WCF service:

[DataContract]
public class Programmer
{
    [DataMember]
    public List<string> ProgrammingLanguagesKnown = new List<string>();

    [DataMember]
    public string Name = string.Empty;
}

Let's say your client calls your method called GetProgrammer() and you return the client a Programmer object with a Name of "Anderson Imes", but with an empty collection of ProgrammingLanguagesKnown. 

Now, let's say the client sends that very same object back to you in a method called AddCSharp:

   1: [OperationContract]
   2: public void AddCSharp(Programmer programmer)
   3: {
   4:     programmer.ProgrammingLanguagesKnown.Add("C#");
   5:     DataLayer.SaveProgrammer(programmer);
   6: }

If you can ignore the fact that this is a completely contrived example for a moment, there is something you must know about Line #4: You are going to get a NullReferenceException on this line.

How is this possible?  ProgrammingLanguagesKnown is always initialized to a new empty List<string>, right?  Painfully, no, this isn't the case.  The reason is that deserialization breaks all of the rules for instantiating objects.  No initializers are run and no constructors are called, even default ones.

Solution

So if constructors aren't called and initializers aren't fired, what can you do?  You have two options.

Option #1: Put sanity checks before you reference ProgrammingLanguagesKnown

[OperationContract]
public void AddCSharp(Programmer programmer)
{
    if(programmer.ProgrammingLanguagesKnown == null)
    {
        programmer.ProgrammingLanguagesKnown = new List<string>();
    }
    programmer.ProgrammingLanguagesKnown.Add("C#");
    DataLayer.SaveProgrammer(programmer);
}

So this is a solution outside of the class itself.  It has to be done everywhere you touch an incoming object of this type, otherwise you risk getting a NullReferenceException.

(This sanity check could easily be put in the get accessor of a property that encapsulates the field, but you get the idea).

Option #2:  Create an OnDeserializing Method to create the empty collection

[DataContract]
public class Programmer
{
    [DataMember]
    public List<string> ProgrammingLanguagesKnown = new List<string>();

    [DataMember]
    public string Name = string.Empty;

    [OnDeserializing]
    public void InitializeTheUninitialized(StreamingContext context)
    {
        ProgrammingLanguagesKnown = new List<string>();
    }
}

This method will ensure that before the properties and fields are populated with the source data from the client, that you get a chance to initialize a field.  This is nice because the code to combat the solution isn't duplicated all over the place.

Trackbacks

No Trackbacks

Comments

No Comments

Leave a Comment