This post is part of a series about WCF extensibility points. For a list of all previous posts and planned future ones, go to the index page.
This post takes a detour from the normal flow of the series, since I was working on a scenario last week which I think will add value to the series as a whole. On the post about contract behaviors, I showed an example of how to hook up a custom serializer to the WCF pipeline. That sample used the DataContractSerializerOperationBehavior extensibility in which you could inherit from that class, override the CreateSerializer methods and replace that behavior on the operation with your own custom class. That’s fairly simple and has been done extensively in WCF. However, this doesn’t work in Silverlight. This post isn’t about one specific extensibility point, but about a scenario which can be covered by the extensibility points already covered in this series, in a different platform.
The WCF portions of Silverlight is a subset (*) of the full framework. Due to many restrictions in the Silverlight platform, such as the sandbox, support for multiple OS, and primarily size (the runtime download time could not be too large, otherwise people might give up installing it), many features in the full WCF didn’t make it into SL. Other features actually went in, but in order to reduce the (large) number of extensibility points, they were made internal. The class DataContractSerializerOperationBehavior is one of those which didn’t make the cut, and for most of the cases it hasn’t been a problem – SL has been released for about 3 years now and only recently I’ve seen a couple of requests for being able to replace the serializer in SL.
(*) It’s not really a proper subset, as there are some features in SL which aren’t available in the full framework, such as the classes in the System.Json namespace; they may eventually make it to the framework, but at this time they’re only available in the WCF Codeplex site.
The first thing to be able to replace the DataContractSerializerOperationBehavior in SL is to actually find out what it does at the client side. I’ve been using Reflector (while it’s free) and it shows that the ApplyClientBehavior method does two things: determines whether the operation input / output should be serialized (if the operation uses untyped messages, then no serialization is necessary, and it then creates the client formatter which will be used to serialize / deserialize the request / reply. So like in the desktop case, our new operation behavior will do something similar. Also, like in the DataContractSerializerOperationBehavior, I’ve made the method CreateSerializer virtual, so that it can be extended to easily replace the serializer.
And before I go further, the usual disclaimer: this is a sample, not production-ready code. I tested it for a few contracts and it worked, but I cannot guarantee that it will work for all scenarios (please let me know if you find a bug or something missing). Also, for simplicity sake it doesn’t have a lot of error handling which a production-level code would, and doesn’t support all contract types (operations with ref / out parameters or duplex contracts, for example, are not supported; headers / properties from the operation context are also not added to the message; I’ve also not tested for unwrapped message contracts – it may work though).
Now for the client formatter. To be able to call the CreateSerializer method in the behavior, the formatter will hold a reference to its “parent”, as well as the operation description (which will be used to serialize the request and deserialize the response). The known types is stored once to be passed to the serializers for the parameter / return values.
In order to serialize the request, I’ve created a new BodyWriter class which first writes the operation wrapper, then it writes each parameter, wrapped by its appropriate name, using the serializer created by the SLSerializerOperationBehavior.
Deserializing is a little simpler, and can be done in a single method. The body reader will first skip the wrapper name (<operationResponse>), then it will use the serializer from SLSerializerOperationBehavior to deserialize the return value. If this code were to support out / ref parameters, we’d need to then iterate through the body parts for the response message and deserialize those values into the parameters argument of DeserializeResponse.
Now to put it all together: just like on the original sample, I’ll use a contract behavior to replace the DataContractSerializerOperationBehavior. However, since that class isn’t public in Silverlight, I needed to add a name check (a small hack). I’m also using some compiler directive to share the same file between the desktop and the Silverlight projects.
And that’s it. In the [Code in this post] link I have the same Order / Product / MyNewSerializer objects from the previous sample.
Back to the normal routine, with instance providers.
[Code in this post]
[Back to the index]