Programming .NET Components : Serialization Events (part 2) - Serialization Events and Class Hierarchies, Serialization and Versioning

5/8/2013 9:21:32 PM

2. Serialization Events and Class Hierarchies

A significant advantage of using attributes for event-handler identification (as opposed to interfaces) is that the event mechanism is decoupled from the class hierarchy. When using attributes, the event-handling methods are called for each level in a class hierarchy. There is no need to call your base class's event-handling methods, and there is no problem if those base methods are private. The events are raised according to the order of the hierarchy, and the event attributes are not inherited. For example, when serializing an object of type MySubClass, defined as:

    public class MyBaseClass

       void OnSerializing1(StreamingContext context)
    public class MySubClass : MyBaseClass
       void OnSerializing2(StreamingContext context)

the OnSerializing1( ) method is called first, followed by a call to OnSerializing2( ). The situation could therefore get messy when virtual methods are involved and the subclass overrides its base class's handling of a serialization event, and even calls it. To deal with this problem, the serialization infrastructure throws an exception of type SerializationException if any of the event attributes are applied on a virtual or abstract method or on an overriding method.

The serialization event attributes throw a SerializationException when applied on a class method that implements an interface method and are ignored when applied to a method in the interface definition.

Use of the new inheritance qualifier is still allowed in conjunction with serialization events. Since no other party besides .NET should call a serialization event-handling method, I recommend always designating such methods as private.

2.1. Using the deserializing event

Since no constructor calls are ever made during deserialization, the deserializing event-handling method is logically your deserialization constructor. It is intended for performing some custom pre-deserialization steps—typically, initialization of non-serializable members. Any value settings done on the serializable members will be in vain, because the formatter will set those members again during deserialization, using values from the serialization stream. The main difference between IDeserializationCallback and the deserializing event is that IDeserializationCallback's OnDeserialization( ) method is called after deserialization is complete, while the deserializing event is called before deserialization starts. You should only place in the deserializing event-handling method any initialization steps that are independent of the values saved in the serialization stream. In contrast, in OnDeserialization( ) you can take advantage of already deserialized members . Other steps you can take in the deserializing event-handling method are setting specific environment variables (such as thread local storage), performing diagnostics, or signaling some global synchronization events.

2.2. Using the deserialized event

Taking advantage of the deserialized event makes the use of IDeserializationCallback redundant, as the two are logically equivalent—both let your class respond to the post-deserialization event and initialize or reclaim non-serializable members, while using already deserialized values.

Example 2. Initializing non-serializable resources using the deserialized event
public class MyClass
   IDbConnection m_Connection;
   string m_ConnectionString;

   void OnDeserialized(StreamingContext context)
      m_Connection = new SqlConnection(  );
      m_Connection.ConnectionString = m_ConnectionString;
      m_Connection.Open(  );

3. Serialization and Versioning

An application may wish to serialize the state of multiple objects of multiple types to the same stream. Consequently, a simple dump of object state will not do—the formatter must also capture each object's type information. During deserialization, the formatter needs to read the type's metadata and initialize a new object according to the information serialized, populating the corresponding fields. The easiest way to capture the type information is to record the type's name and reference in its assembly. For each object serialized, the formatter persists the state of the object (the values of the various fields) and the version and full name of its assembly, including a token of the assembly's public key (if a strong name is used). 

The formatters by default comply with .NET's general version-binding and resolving policy. If the serialized type's assembly does not have a strong name, the formatters try to load a private assembly and completely ignore any version incompatibility between the version captured during serialization and the version of the assembly found. If the serialized type's assembly has a strong name, .NET insists on using a compatible assembly. If such an assembly is not found, .NET raises an exception of type FileLoadException.

Both the binary and SOAP formatters also provide a way to record only the friendly name of the assembly, without any version or public-key token, even if the assembly has a strong name. The formatters provide a public property called AssemblyFormat, of the enum type FormatterAssemblyStyle, defined in the System.Runtime.Serialization.Formatters namespace:

    public enum FormatterAssemblyStyle
    public sealed class BinaryFormatter : IFormatter,...
       public FormatterAssemblyStyle AssemblyFormat{get; set;}
       //Other members, including implementation of IFormatter
    public sealed class SoapFormatter : IFormatter,...
       public FormatterAssemblyStyle AssemblyFormat{get; set;}
       //Other members, including implementation of IFormatter

Note that the AssemblyFormat property is not part of the IFormatter interface. The default value of AssemblyFormat is FormatterAssemblyStyle.Full. If you set it to FormatterAssemblyStyle.Simple, no version-compatibility checks will take place during deserialization. For example, consider this SOAP serialization code:

    MyClass obj = new MyClass(  );
    obj.Number1 = 123;
    obj.Number2 = 456;

    SoapFormatter formatter = new SoapFormatter(  );
    formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;

    Stream stream;
    stream = new FileStream(@"C:\temp\obj.xml",FileMode.Create,FileAccess.Write);


This code results in the following SOAP envelope body:

       <a1:MyClass id="ref-1"

Although this option exists, I strongly discourage you from circumventing version serialization and type verification. At best, a potential incompatibility will result in an exception of type SerializationException. At worst, your application may later crash unexpectedly because the incompatible type required some custom initialization steps.

Top 10
Free Mobile And Desktop Apps For Accessing Restricted Websites
TOYOTA CAMRY 2; 2.5 : Camry now more comely
KIA SORENTO 2.2CRDi : Fuel-sipping slugger
How To Setup, Password Protect & Encrypt Wireless Internet Connection
Emulate And Run iPad Apps On Windows, Mac OS X & Linux With iPadian
Backup & Restore Game Progress From Any Game With SaveGameProgress
Generate A Facebook Timeline Cover Using A Free App
New App for Women ‘Remix’ Offers Fashion Advice & Style Tips
SG50 Ferrari F12berlinetta : Prancing Horse for Lion City's 50th
Popular Tags
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone