How Does Wcf Deserialization Instantiate Objects Without Calling a Constructor

How does WCF deserialization instantiate objects without calling a constructor?

FormatterServices.GetUninitializedObject() will create an instance without calling a constructor. I found this class by using Reflector and digging through some of the core .Net serialization classes.

I tested it using the sample code below and it looks like it works great:

using System;
using System.Reflection;
using System.Runtime.Serialization;

namespace NoConstructorThingy
{
class Program
{
static void Main()
{
// does not call ctor
var myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass));

Console.WriteLine(myClass.One); // writes "0", constructor not called
Console.WriteLine(myClass.Two); // writes "0", field initializer not called
}
}

public class MyClass
{
public MyClass()
{
Console.WriteLine("MyClass ctor called.");
One = 1;
}

public int One { get; private set; }
public readonly int Two = 2;
}
}

http://d3j5vwomefv46c.cloudfront.net/photos/large/687556261.png

Is it possible to serialize objects without a parameterless constructor in WCF?

You can't really make arbitrary types serializable; in some cases (XmlSerializer, for example) the runtime exposes options to spoof the attributes. But DataContractSerializer doesn't allow this. Feasible options:

  • hide the classes behind your own types that are serializable (lots of work)
  • provide binary formatter surrogates (yeuch)
  • write your own serialization core (a lot of work to get right)

Essentially, if something isn't designed for serialization, very little of the framework will let you serialize it.

How to deserialize class without calling a constructor?

  1. You could create a class that inherits from CustomCreationConverter
    and use FormatterServices.GetSafeUninitializedObject to create your
    object. It skips calling the constructor.

    More about CustomCreationConverter here.

  2. Placing
    [JsonObject(MemberSerialization.Fields)] on a class will make Json.NET use
    FormatterServices.GetSafeUninitializedObject by default (although
    Fields mode will also serialize public/private fields rather than
    public properties which you may not want).

  3. Move the logic you don't want run outside of the default constructor.

WCF Services and Object Constructors

The answer will depend on which serialization model your type GpsPosition uses. The two most common ones used in WCF are POCO (plain-old CLR object) and [DataContract]. In the former, the object must have a parameter-less constructor, which goes against your requirement (that the values need to be set once). In the latter, the object constructor is not invoked - instead an uninitialized instance of the type is created, and its members are set via deserialization.

So constructors are not an alternative to validate the objects coming from the wire. What you need to validate the object, instead of the constructor, is a serialization callback, which is invoked when the deserialization is complete. The WCF serializer will call them when the deserialization is done, and there you can check whether the object was properly initialized, and throw an exception otherwise. This blog post has more details on serialization callbacks, and the code below shows one possible implementation for your scenario.

[DataContract]
public class GpsPosition
{
private float _lat;
private float _lon;
private bool _latWasSet;
private bool _lonWasSet;

public GpsPosition(float lat, float lon)
{
_lat = lat;
_lon = lon;
}

[DataMember]
public float lat
{
get { return _lat; }
private set
{
_lat = value;
_latWasSet = true;
}
}

[DataMember]
public float lon
{
get { return _lon; }
private set
{
_lon = value;
_lonWasSet = true;
}
}

[OnDeserialized]
void OnDeserialized(StreamingContext ctx)
{
if (!_latWasSet || _!lonWasSet ||
_lat < -90 || _lat > 90 ||
_lon < -180 || _lon > 180)
{
throw new InvalidOperationException("Required property is missing");
}
}
}

Field Initializer in C# Class not Run when Deserializing

On deserialization neither the constructors nor the field initializers are called and a "blank" un-initialized object is used instead.

To resolve it you can make use of the OnDeserializing or OnDerserialized attributes to have the deserializer call a function with the following signature:

void OnDeserializing(System.Runtime.Serialization.StreamingContext c);

In that function is where you can initialize whatever was missed within the deserialization process.

In terms of convention, I tend to have my constructor call a method OnCreated() and then also have deserializating method call the same thing. You can then handle all of the field initialization in there and be sure it's fired before deserialization.

[DataContract]
public abstract class MyAbstract
{
protected Dictionary<int, string> myDict;

protected MyAbstract()
{
OnCreated();
}

private void OnCreated()
{
myDict = new Dictionary<int, string>();
}

[OnDeserializing]
private void OnDeserializing(StreamingContext c)
{
OnCreated();
}

private bool MyMethod(int key)
{
return myDict.ContainsKey(key);
}

private int myProp;

[DataMember]
public int MyProp
{
get { return myProp; }
set { bool b = MyMethod(value); myProp = value; }
}
}

DataContractSerializer doesn't call my constructor?

DataContractSerializer (like BinaryFormatter) doesn't use any constructor. It creates the object as empty memory.

For example:

    Type type = typeof(Customer);
object obj = System.Runtime.Serialization.
FormatterServices.GetUninitializedObject(type);

The assumption is that the deserialization process (or callbacks if necessary) will fully initialize it.

Why is my abstract base class's constructor not called when an object is initialized by the WCF deserializer?

WCF (and DataContractSerializer in particular) doesn't use constructors. No, really (it uses FormatterServices.GetUninitializedObject to create raw objects).

It is expected that all data will be initialized either by the serializer, or for non-serialized fields - by serialization callbacks that you add (for example, via [OnDeserialized]).

WCF - Instantiating an object in DataContract constructor

If you use the default DataContractSerializer to serialize your objects, then, yes, your constructor is not serialized, and any logic you may have in it will not be called by your client when the object is deserialized.

Regarding your question about removing the constructor logic and having the nested Address class be populated, that will be taken care of by the DataContractSerializer. If I have code like this:

Customer c = new Customer() { 
FirstName = "David",
LastName = "Hoerster",
CustomerAddress = new Address() {
Line1 = "1 Main Street",
City = "Smallville",
State = "AA",
Zip = "12345"
}
};

and then return that from a service method, that Customer object will be serialized properly along with the Address information. The proxy on the client that's generated will know about Address and will be able to deserialize the stream coming from the service method to properly construct your Customer object. Your Customer will be a dummy DTO -- no logic, just properties.

Check out Aaron Skonnard's MSDN article on WCF Serialization where he talks about the DataContractSerializer.



Related Topics



Leave a reply



Submit