The other day I wrote a bit about the issue with read-only collection properties and XmlSerializer . Today I will write about a similar issue, but this time it is the relation between read-only collections and the DataContractSerializer. The symptom was similar, but the cause and the solution entirely different. Well, it's not strictly the relation between read-only collections and the DataContractSerializer, but that was the scenario that led me to stumble upon the behavior that led to this blog post.

Let's start with the Manager sample that the previous post resulted in (here decorated with attibutes to allow it to be exposed through a WCF service):

[DataContract]
public class Manager : Employee
{
    private List<Employee> _subordinates;;

    public Manager()
    {
        _subordinates = new List<Employee>();
    }

    [DataMember]
    public List<Employee> Subordinates
    {
        get
        {
            return _subordinates;
        }
    }
}

While the above class serializes and deserializes without any issues, passing an instance of this class in a WCF service call generates a FaultException with a message that I found a bit puzzling:

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:manager. The InnerException message was 'The get-only collection of type 'System.Collections.Generic.List`1[[WcfService1.Employee, WcfService1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' returned a null value. The input stream contains collection items which cannot be added if the instance is null. Consider initializing the collection either in the constructor of the the object or in the getter.'. Please see InnerException for more details.

I found the advice in the end particularly interesting: Consider initializing the collection either in the constructor of the the object or in the getter.. Isn't that exactly what the constructor does? This advice is also interesting given that the following can be found in the DataContractSerializer documentation:

When instantiating the target object during deserialization, the DataContractSerializer does not call the constructor of the target object.

...and...

If you code the class that assumes the constructor has run, the class may get into an invalid internal state that is exploitable.

Clearly, the constructor is not the way to go on this one. Enter OnDeserializing attribute. This attribute can be used to decorate a method that will be executed by the serializer during deserialization. This mechanism can be used to initialize fields when the constructor is not run. Time to refactor the class again:

[DataContract]
public class Manager : Employee
{

    private List<Employee> _subordinates;

    public Manager()
    {
        Initialize();
    }

    [OnDeserializing]
    private void OnDeserializing(StreamingContext context)
    {
        Initialize();
    }
    
    private void Initialize()
    {
        _subordinates = new List<Employee>();
    }
    
    [DataMember]
    public List<Employee> Subordinates
    {
        get
        {
            return _subordinates;
        }
    }
}
 

Now, the collection will be initialized regardless of whether a Manager object is constructed in a normal manner, or if it is created during serialization in a manner that bypasses the constructor.

Bookmark and Share

I had an issue serializing a class using the XmlSerializer, where the class had a collection property. It wasn't really a problem from the start (not with serializing anyway). The problem was that the class exposed the collection property as a read/write property, which is violating the recommendation that collection properties should be readonly (the Manager/Employee sample here is merely for illustration purposes):

public class Employee
{
    public string Name { get; set; }
}

public class Manager : Employee
{
    public Manager()
    {
        Subordinates = new List<Employee>();
    }
    public List<Employee> Subordinates { get; set; }
}

So I did some refactoring, making the property readonly (which led to some minor updates of calling code that assigned a new collection to the property). The updated Manager class looked like this;

public class Manager : Employee
{
    public Manager()
    {
        Subordinates = new List<Employee>();
    }
    public List<Employee> Subordinates { get; private set; }
}

So far all was good. Then I ran the unit tests, and got an InvalidOperationException when an XmlSerializer attempted to serialize the object:

Unable to generate a temporary class (result=1). error CS0200: Property or indexer '[namespace].Manager.Subordinates' cannot be assigned to -- it is read only

What? Does the XmlSerializer class not support the recommended way of exposing collection properties? So we move on to do some reading and find this in the document describing the FxCop rule Collection properties should be read only:

Both binary and XML serialization support read-only properties that are collections. The System.Xml.Serialization.XmlSerializer class has specific requirements for types that implement ICollection and System.Collections.IEnumerable in order to be serializable.

Here is specifically states that it should be OK to expose the collection as a read-only property and still have the XmlSerializer serialize it. So how do I get this to work? For some reason I decided to move away from the auto property approach and try how it would behave then:

public class Manager : Employee
{
    private List<Employee> _subordinates;
    
    public Manager()
    {
        _subordinates = new List<Employee>();
    }
    
    public List<Employee> Subordinates
    {
        get
        {
            return _subordinates;
        }
    }
}

And suddenly it worked. I found this behaviour slightly odd, especially since XmlSerializer does not serialize private fields, so I don't quite see how it would make a difference that the collection property has a manually created backing field, or a private set accessor.

Update: As Marc points out in the comments below; the XmlSerializer also fails if you add a private setter for the property, so this is not an issue with auto-properties as such, but you can’t resolve it using an auto-property.

Update 2: I decided to file this as a bug. Don't hesitate to vote for it if this may pose a problem for you as well.

kick it on DotNetKicks.com

Bookmark and Share

In c#, when you want to declare a field holding a specific, static value for all instances of a type, there are two ways to achieve this: either you declare the field static readonly, or you declare a const. But what is the difference between them?

Take the following code:

class Sample
{
   public const int TheConst = 1;
   public static readonly int TheStaticReadonly = 1;
}

Looking at the IL code, the two members generate the following:
.field public static literal int32 TheConst = int32(0x00000001)
.field public static initonly int32 TheStaticReadonly

As we can see, there are two concrete differences:

  • TheConst is declared as a literal, and has an int32 value assigned to it
  • TheStaticReadonly is declared as a initonly , and has no value assigned to it

The assignment of TheStaticReadonly has been moved to the static constructor. This means that the code sample above generates identical byte code as the following:

class Sample
{
    public const int TheConst = 1;
    public static readonly int TheStaticReadonly;

    static Sample()
    {
        TheStaticReadonly = 1;
    }
}

That means that if a type has a const declared, its value is known as compile time, while the value of a static readonly field cannot be known at compile time. This is a small, but important difference. For instance, it means that you cannot use the value of a static readonly field anywhere in your code where a constant value is expected:

int n = 1;
switch (n)
{
    case Sample.TheConst:
        {
            break;
        }
    case Sample.TheStaticReadonly: // compiler error CS0150 ("A constant value is expected") 
        {
            break;
        }
}
Bookmark and Share