21/09/2016

Report from the battlefield #6 - Auto-Property Initializers + a non-binary serialization

Home


Source: own resources, Authors: Agnieszka and Michał Komorowscy

I can bet that you've already heard about and used Auto-Property Initializers and that you love them. At least I do so ;) Here is a small example. In this case an Auto-Property Initializer was used to generate unique identifiers for instances of Entity class. Trivial, isn't it?
public class Entity
{
   public string Guid { get; } = System.Guid.NewGuid().ToString();
   /*...*/
}
What is important we have guaranteed that a given initializer will be executed only ONCE for an instance of a class. Otherwise it will have no sense! In other words our expectations is that if we create a new instance of Entity class it's identifier will not change. It is generally true, but there are some caveats.


The problem may occur if we use a non-binary serialization in order to serialize and deserialize the same object e.g.: XML or JSON serialization. The difference between binary and non-binary serialization is that the latter creates a completely new object during the deserialization process. Is it bad?

Sometime ago I fixed the following issue. Let's consider a situation where we have a distributed system. Additionally, we keep instances of Entity class in a distributed cache. Each node of this system should be able to retrieve an entity from a cache based on it's identifier. Where is a problem? Each time when someone tries to retrieve an entity from the cache, a new instance with a new identifier will be created. In other words, we'll read from a cache a different object than was put there.

Let's examine Auto-Property Initializers more carefully. They are actually a syntactic sugar and under the hood the compiler generates additional code for us. Let's see what code will be generated for Guid property:
public class Entity
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly string <guid>__BackingField;

    public string Guid
    {
      get
      {
        return this.__BackingField;
      }
    }

    public A()
    {
      this.<guid>__BackingField = System.Guid.NewGuid().ToString();
    }
}
There is no magic here, Guid property is initialized in a constructor. Now, it should be obvious why a new identifier is generated when a new instance is created by the non-binary deserialization. A solution is to generate identifiers outside of Enity class i.e. do not use Auto-Property Initializer.

Of course, we don't have to use Auto-Property Initializers to have this problem. However, I think that with Auto-Property Initializers it's easier to make this kind of a mistake.

0 comments:

Post a Comment