Miscellaneous Debris

Avner Kashtan's Frustrations and Exultations

WCF Serialization part 1: Interfaces, Base classes and the NetDataContractFormatSerializer

One of WCF's goals is interoperability with standard protocol stacks, like WSE or other WS-* implementations. This led, I am told, to the design of WCF's default object serializer - the DataContractFormatSerializer. This handy little engine serializes primitive types, common objects, enums, Collections and anything that implements IXmlSerializable or ISerializable, giving us a much nicer out-of-the-box-and-over-the-wire experience for our objects.

Because of the aforementioned design goal, however, it has one little problem - the serialized data it generates does not contain information about the .NET CLR type from which this data was serialized. This makes sense when you have interoperability in mind - the datatypes you pass should be described in your service metadata, such as WSDL, and not rely on an implementation detail like the CLR type itself.

When we're writing closed systems, however, where we control both ends of the pipe and use WCF code, it can get limiting. Conside this scenario:

[ServiceContract]
interface MyService
{
   [OperationContract]
   void SendMessage (IMessage message);
}

This is a pretty realistic simplification of my current system, and we often have interfaces or abstract base classes defined in our service contracts.

What happens now? Let's I try to send a concrete IMessage implementations to this contract. When the service receives the messages and tries to deserialize the message parameter, it's stuck - there's no way to create an abstract IMessage from the serialized data, and the data carries no information on how to deserialize it.
If my contract defined this:

 [OperationContract]
   void SendMessage (MessageBase message);

I would be in the same jam - it would try to deserialize a DerivedMessage object into a MessageBase - at best, there would be loss of data. At worst (and as it happens) it simply fails to work.

The first solution that WCF's object model offered is to explicitly mark the service with the concrete types it can receive. This is a stop-gap measure that works, but takes the whole point out of using interfaces and inheritance trees:

[OperationContract]
[ServiceKnownType(typeof(DerivedMessage)]
void SendMessage (MessageBase message);

This would require me to add a ServiceKnownType attribute for every single concrete type, past, present and future. Obviously, not a good solution.

An alternate method involved a daring bit of magic:

[OperationContract]
[ServiceKnownType("MyKnownTypeMethod")]
void SendMessage (MessageBase message);

In this case, some backstage voodoo looks for a static method of that given name, calls it during the type's static constructor and asks it to return a list of Known Types. Not only is this totally unintuitive, it also simply doesn't work, at least for me. Maybe it's the beta, maybe it's me.

The third solution came to me from the future. I was shaking my fist at the heavens and asking why couldn't WCF simply serialize the typenames inside, like is done with Remoting. Interop isn't my concern, and it's silly to lose such a powerful feature. My answer came from Aaron Skonnard's blog, and from his August 2006 MSDN Magazine article, not quite yet published:

It seems the standard DataContractFormatSerializer has a shy little brother called the NetDataContractFormatSerializer. The 'Net' here, like in other parts of WCF, means ".NET". This is a formatter than can be used when both ends of the stream are running .NET, and it can thus embed the type information in the serialized blob. This makes all the fudging around with knowntypes completely unneccesary, and gives us the flexibility we had with earlier methods.

Using it, unfortunately, is a closely held secret. Skonnard's blog is the only mention of the name outside of the sketchy SDK documentation. It seems that we have to write our own OperationBehavior attribute (which Skonnard graciously supplies) and put it on our OperationContracts. The code can be found in his page, and usage is as follows:

[ServiceContract]
interface MyService
{
   [OperationContract]
   [NetDataContractFormat]
   void SendMessage (IMessage message);
}

And that's it. The new attribute will add code that will replace the Formatter used during serialization, and from now on we're on easy street.

Notice that we have to set this attribute on every operation we need it. I was originally frustrated with not getting it to work because I instinctively put the attribute not on the SendMessage operation but rather on the IMessage definition - it stood to reason that we put it on the serializee, not the serializer. Once I got my head wrapped around that, it turned out you can't put it on the ServiceContract but have to do it for each operation seperately.

It seems a lot of work, but it really is much simpler than the alternatives above. Thank you, Aaron, for revealing this hidden gem! 

 

Comments

Claudio said:

Hello,

I just tried this but I get a - {"Encountered unexpected node SagaObject while deserializing parameters."}

here is my test:

   [ServiceContract(Session = true)]

   public interface IDBService

   {

       [OperationContract]

       [NetDataContractFormat]

       SagaObjects.SagaObject ReadObject();

   }

   [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]

   public class DBService : IDBService

   {

       public SagaObjects.SagaObject ReadObject()

       {

           try

           {

               SagaObjects.SagaObject o = new SagaObjects.SagaObject();

               return o;

           }

           catch (Exception ex)

           {

// todo

           }

       }

   }

Thank you for any suggestion...

# August 9, 2006 12:53 PM

Civa said:

This is really nice article thank you for posting this.It solved some of my problems and of course thanks to Aaron Skonnard he is God!

Happy coding!

Civa

# October 21, 2008 6:55 PM

Robbie said:

You just solved a major implementation issue for me.  Thanks!!

# December 5, 2008 11:44 AM

Joe said:

Awesome, and Thanks!

# August 3, 2009 10:48 PM

the rasx() context » Blog Archive » A Monday Table of WCF Links said:

Pingback from  the rasx() context  » Blog Archive   » A Monday Table of WCF Links

# December 1, 2009 12:54 AM

Bandaster said:

Hi, we're using this but find that the client is holding on to instances of NetDataContractFormat each time a call is made.  This results in a large memory leak after multiple service calls.  It's more than likely something we're doing but I was wondering if anyone else has seen this?  Thanks.

# February 17, 2010 7:54 AM

Bandaster said:

Just a follow up on previous post re the memory leak, this turned out to be nothing to do with NetDataContractFormat.  I was thinking it might have been as I was seeing lots of instances of this not being garbage collected.  As it turned out the proxy was not going through garbage collection either.  This was due to an instance of OperationContextScope being created and not disposed of.

Bandaster

# March 9, 2010 5:54 PM

Problem with using IEnumerable types as return types in WCF - Programmers Goodies said:

Pingback from  Problem with using IEnumerable types as return types in WCF - Programmers Goodies

# September 26, 2011 12:05 AM

SussyGunish said:

Cool article to my mind. Keep writing this way!

Sussy Gunish

<a href="delhiescortservice.com/">escorts service in delhi</a>

# November 3, 2011 12:17 PM

Luiz said:

Hi there.

Please, where could I find the NetDataContractFormat attribute if I'm working in a VB.NET project running on Framework 4? I've just found DataContractFormat and I'm getting the same error yet.

I used the following attributes: OperationContract, ServiceKnownType e DataContractFormat as the following:

<OperationContract()> _

<ServiceKnownType(GetType(CentroResultado))> _

<DataContractFormat()> _

Function ListPlaces(ByVal id As Integer) As IEnumerable(Of Place)

Any suggestion?

Thanks a lot!!!

# November 18, 2011 6:06 AM

lofeashton said:

check this link,   for less <a href=codysimpsonteam.com/.../index.php  , for special offer

# January 16, 2012 10:20 PM

Ceteletitia said:

buy best   for more detail <a href=www.audiforums.com/.../a>  online

# January 18, 2012 9:14 AM

bahAxoda said:

order an <a href=chanel-bags-for-cheap.weebly.com/>chanel bags for cheap</a>  online shopping

# January 28, 2012 12:10 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)