Posts Tagged ‘m’
Advanced serialization tips
Seralization knowledge not fully covered by official training kit. These are actually notes from CLR via C# chapter 24.
1. SoapFormatter as of .NET 3.5 is obsolete, for production code use XmlSerializer or DataContractSerializer.
2. Instances can be deep cloned using binary serialization, remember to set:
bin_formatter.Context = new StreamingContext(StreamingContextStates.Clone);
3. You can serialize multiple objects to single stream
4. If You serialize instances of objects which are located in custom loaded assemblies (using LoadFrom), then SerializationExceptin might be thrown. To load assembly when requested, implement delegate that matches System.ResolveEventHandler and register this method with System.AppDomain’s AssemblyResolve event just before deserialization and remove it after it.
5. If some of the objects in the graph are not serializable, then exception will be thrown. It is thrown not before serialization, it can happen any time during it. This can cause resulting stream to contain corrupt data. In order to fail gracefully, serialize to memory stream, and if all is ok, send it to network stream etc.
6. The SerializableAttribute custom attribute may be applied to reference types (class), value types (struct), enumerated types (enum), and delegate types (delegate) only. But since enumerated types and delegates are serializable by default, it makes no sense to add this attribute to them.
7. Serializable attribute is not being inherited, if super class is non serializable then subclasses are not either.
8. Do not set Serializable to automatically generated properties, their names can change during compilation, making it not possible to deserialize.
9. Formatters makes use of FormatterServices class for serialization and deserialization, steps are as follows:
Serialization:
– Call FormatterServices.GetSerializableMembers to get MemberInfo[].
– Then call FormatterServices.GetObjectData to get array of values for each MemberInfo instance in the above array.
– Store assembly identity and type’s full name.
– Store each member, their type, name and value.
Deserialization:
– Load assembly identity information and if required load it. Call FormatterServices.GetTypeFromAssembly. This call returns System.Type that represents object to be deserialized.
– Call FormatterServices.GetUninitializedObject with previously returned System.Type, this returns an non-constructed object, with fields set to zero values.
– Now call FormatterServices.GetSerializableMembers to get MemberInfo[]. Those members will need to be deserialized.
– Formatter creates object data from the stream, those will be values of our members.
– With all prepared data call FormatterServices.PopulateObjectMembers, it requires reference to object, MemberInfo[] and Object[] – with values (deserialized in step above).
10. Implementation of ISerializable interface (to get freedom of how serialization happens and get rid of slow reflection), requires implementing its methods in each subclass, and also calling in them, super class versions.
11. Always add followng attribute to your ISerializable.GetObjectdata: [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]. This will prevent outside code from using this method in some malicious ways.
12. When using SerializationInfo class in c-tor, you should call the same Get* methods types as in ISerializable.GetObjectData. If there is a mismatch when deserializing given data, framework will try to convert it with IFormatterConverter, which internally calls System.Convert for core types. For other types it uses type’s IConvertible implementation.
13. SerializationInfo also provides enumerator, which returns SerializationEntry instances.
14. To custom serialize class which base type does not implement ISerializable, use this.GetType().BaseType, and serialize/deserialize it manually. This might be not possible with private fields.
15. Serializing a Type as a different Type, and Deserializing as a different object. This is of use when using singletons, DBNull object. For serialization:
– Add to ISerialization.GetObjectData, SerializationInfo.SetType() method call. As a parameter specify type of custom class that derives from IObjectReference.
16. Serialization surrogates: this is a technique that allows to serialize types not designed for serialization, to deserialize type to a new version, etc.. It is realized by createing class that implements ISerializationSurrogate interface. Its methods GetObjectData and SetObjectData are used for serialization and deserialization. In orderd to let Formatter know about our serializing surrogate implementation, create and initilize SurrogateSelector class and set it to Formatters SurrogateSelector property.
17. The BinaryFormatter class has a bug that prevents a surrogate from serializing objects with references to each other. For fix look into CLR Via C#.
18. Finally there is a technique to deserialize data to a different object than the one used during serialization. This is of use when new version of type is created, or type is moved to other assembly, client want to deserialize data from server to different local type. To use it implement SerializationBinder abstract class, and set it to Formatter’s Binder property.
Below are notes from other sources:
19. ObjectManager tracks deserialized objects. Formatter uses it to check if given reference is backward reference – its object was already deserialized, or it is forward reference – instance was not yet deserialized – this requires recording fixup with ObjectManager. Fixup means that reference will be assigned correct object in the future when actual object will get deserialized.
20. Marshaling is the process of packaging and sending remote procedure calls across process and application boundaries using serialization and deserialization.