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?
You could create a class that inherits from
CustomCreationConverter
and useFormatterServices.GetSafeUninitializedObject
to create your
object. It skips calling the constructor.More about CustomCreationConverter here.
Placing
[JsonObject(MemberSerialization.Fields)]
on a class will make Json.NET useFormatterServices.GetSafeUninitializedObject
by default (although
Fields mode will also serialize public/private fields rather than
public properties which you may not want).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
How to Multi-Target a .Net Core Class Library with Csproj
Grid Lines Are Not Displaying in Grid View
How to Call a R Statistics Function to Optimize C# Function
Send Email Using System.Net.Mail Through Gmail
How to Manipulate Images at the Pixel Level in C#
5.7.57 Smtp - Client Was Not Authenticated to Send Anonymous Mail During Mail from Error
Passing Dynamic Object to C# Method Changes Return Type
How to Loop Through a Date Range
Visual Studio: Contextswitchdeadlock
How to Send HTML-Formatted Email
Difference Between Await and Continuewith
Bind Textbox on Enter-Key Press
How to Fast Get Hardware-Id in C#
Retrying Httpclient Unsuccessful Requests
How to Get Linq to Return the Object Which Has the Max Value for a Given Property