Posts Tagged ‘Remoting’

Serialization With .Net Remoting - Chronicles Of A Solution

Friday, December 28th, 2007

.Net offers two great technologies - Serialization and Remoting.
Both work almost seamlessly independently, but should you try to use both, sequentially, you are into trouble.

There is a feedback issue on Microsoft’s Connect site that depicts this exact problem:
Serialization of MarshalByRef object fails after a remoting identity has been set
Unfortunately they Closed the issue with a "Won’t Fix" state.
Here is what Microsoft have this to say about the problem:

In 2005 they wrote:

Unfortunately, it was determined that it is now too late in the Whidbey product cycle to change this behavior, given that there is no straightforward solution. Thus, the problem will most likely not be fixed in Whidbey, but we will definitely consider it for the next version of the framework

And in 2007:

We have decided to only invest limited resources into Remoting and Binary Serialization, and concentrate on the Windows Communication Foundation and Data Contract Serialization instead. We’re only proactively fixing the most critical / most customer-impactful issues in the legacy technologies. However, both Remoting and Binary Serialization are of course supported technologies and will be supported for a long time

It is facinating to hear that the inability to combine two widely used .Net framework technologies is not concidered a critical customer impactful issue.
Admitting the problem, the lack of workarounds for it, the request of customers to fix it and the promiss of a future solution, followed by a claim that WCF outweights serialization and remoting in resource allocation, when it cannot fill in the gap, is nothing less than outrageous.

Needless to say that there are straightforward solutions for this problem, the most simple of which was suggested and acknowlegded in the feedback issue - setting the NonSerialized attribute on the Identity field of MarshalByRefObject, as it SHOULD have been in the first place.
When searching for a practical solution, I have even concidered injecting the NonSerialized attribute at runtime, or to modify mscorlib itself using Reflector.

Unwilling to mess with core framework code, the only feasible solution found was to use db4o to perform serialization for me.
How would that help ? db4o claimed and proved that their mechanism overrides the ServerIdentity problem, allowing to perform serialization on objects even after accessing them with remoting.
They achieved this by applying a TTransient translator on all ServerIdentity objects, which in effect removed the non-serializable problematic factor from the serialization process, allowing for both serialization and remoting to take place.

Encouraged by this achievement, I went on to test if the same workaround could be applied directly in the serialization process, so I used a ISerializationSurrogate to control the serialization process of ServerIdentiy, or to be more precise the lack of it.
db4o were able to have active remoting objects serialized by preventing the Identity field from being serialized AND by setting it’s value back to null upon deserialization (the default value of a remoting object not yet accessed from a remote AppDomain).

The phrase "The right hand doesn’t know what the left hand is doing" couldn’t come in any better.
ISerializationSurrogate has a method named SetObjectData:

Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector);

The return value of this method controls what would be set into the deserialized object, as seen fit by the SerializationSurrogate.
Well ….. Almost.
Any value, but null.
By definition, the mechanisms on top of the surrogate passes a dummy object to it, allowing its fields to be set directly or by reflection. In order to allow a different object reference to be set into the deserialized object, a different object can be returned as a return value. How should the all mighty algorithms select the proper one ? Using a null return value indication off course. That is, should the return value be null, the dummy object will be used, and should it be an object reference the returned object will be used.
You guessed it right, you can’t return null as a new value of the surrogated object.

So close to a solution, only to be halted by another Microsoft design flaw.

But this time the problem has shifted, it was no longer an issue of serialization and remoting, but simply a matter of getting one field of one object to be null.
All hail reflection !
Setting the Identity field of any MarhsalByRefObject is easier than a walk in the park, the question is - when ?
The best place to catch a MarshalByRefObject during it’s deserialization process is in the OnDeserialized event of any deserialized object.

So the final solution to this problem is so:

  • Create a new ISerializationSurrogate named TransientSurrogate.
  • Make it do nothing at all.
  • Set the surrogate in your serialization mechanism to override ServerIdentity’s serialization.
  • Derive from MarshalByRefObject and name it SerializableMarshalByRefObject.
  • Set it’s "Identity" field to null using reflection inside the OnDeserialized event.
  • Use SerializableMarshalByRefObject through out your application instead of MarshalByRefObject.
  • Enjoy both serialization and remoting in your appliaction !

I don’t have a working sample to upload, but if you run into trouble just contact me.

Hope this helps those of you who got stuck with the problem.

And my favorite quote, from the same MSDN article about ISerializationSurrogate:

Since surrogates don’t have intimate knowledge of the existing type, surrogate types tend to be useful only for simple types where the developer just plain forgot to make the type serializable.

Microsoft - clean up your mess.


Copyright © 2010 קוצים של פחד. All Rights Reserved.
No computers were harmed in the 6.970 seconds it took to produce this page.

Designed/Developed by Lloyd Armbrust & hot, fresh, coffee.