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.
Continuing on serialization, this post is about a new feature on the WCF serializers in .NET Framework 4.0, the DataContractResolver. Like the previous post on surrogates, there is already good documentation on MSDN, so this post will be short. But since I’ve seen an issue this week on the forums, I thought it’d be worth including the code here in this series.
In order to understand the data contract resolver, we need to take a step back and look at a common issue (and source of confusion) in WCF: known types. In order for WCF to be able to serialize or deserialize an object graph, it needs to know about all the objects in that graph. If the actual type of the object is the same as its declared type, then WCF already knows about it (the “baseInstance” member in the code below). If those types are different, then WCF doesn’t know by default about the type – in the case of the “derivedInstance” in the code below, WCF doesn’t know the MyDerived type, so it won’t serialize it unless we tell it that this type is known.
There are many good descriptions on why we need the known types and how to set them (“official” MSDN documentation, Youssef Moussaoui’s MSDN blog, Mark Gravell’s Stack Overflow post, Richard Blewett’s blog, etc.) so I won’t go in detail here. But known types serves a purpose that we can declare, when the serialize is being created, which types are to be considered "valid” even though they’re not part of the object declaration. And the part in italic is important here – once the known type list is passed somehow to the serializer, it’s set and it cannot be changed during the serializer lifetime.
In the version 4.0 of the framework, WCF introduced a new concept, , the data contract resolver. Instead of defining the set of known types “statically” (either by specifying the types directly in a KnownTypeAttribute – or ServiceKnownTypeAttribute – or by specifying a method in those attributes which will be invoked when the serializer is being configured), the data contract resolver provides some hooks which allow us to specify, at the moment when the object is being serialized or deserialized, a mapping between the CLR type and the name / namespace in the XML which will be used to represent this “unknown” type. There is good documentation about this topic, including the MSDN reference page, the MSDN detail page, or Youssef Massaoui’s blog, so I won’t repeat that information here.
When the serializer is set up with a DataContractResolver, during serialization, if the serializer finds out an object where the actual type doesn’t match the declared type, it will ask the resolver whether it can process it, by calling TryResolveType. If the resolver can do that, then the serializer will use the resulting name and namespace to write out the XML for that type in the type hint attribute (an attribute with local name “type” and namespace “http://www.w3.org/2001/XMLSchema-instance”. During deserialization, if a type hint attribute is found, then the serializer will call the ResolveName method on the resolver, where it can decide which type to use.
Just like with the data contract surrogates shown in last post, the DataContractResolver can be passed as a parameter to the constructor of the WCF serializers (both the DataContractSerializer and the DataContractJsonSerializer). The example below shows one simple example of a resolver in action. Notice that all resolver implementations should delegate the call to the known type resolver parameter passed to it in case it can’t handle the type itself.
For setting the resolver in the context of WCF services, just like with the surrogates, you can use the DataContractResolver property on the DataContractSerializerOperationBehavior class. For an example of accessing this behavior, check the previous post.
The scenario for the data contract resolver is when we want to delay the decision about known (safe?) types until it’s actually needed. I thought for a while but couldn’t think of an interesting scenario for this, until I stumbled upon this post on the forums, where the user wanted to use dynamic types (i.e., those created using the System.Reflection.Emit namespace). That’s a great example of a scenario where the “traditional” known type approach doesn’t work – the actual type may not even exist when the service / serializer is being created. So I created a sample with this scenario. The idea is passing an object between the client and the server which has a member of type System.Object, and the actual value of the field is of a dynamic type.
This is the holder object – nothing fancy here.
And in the service contract I wanted to have three scenarios: one where the client sends a request to the server with a dynamic type (which originally didn’t exist in the server) and receives the same type; one which the client just sends something new to the service, but receives a known type; and one which the server is the one who sends something back to the client that it doesn’t know yet.
The server implementation is straightforward, although the GetHolder method uses a type builder library which I’ll mention later.
And before I go on with more code, the usual disclaimer: this is a sample for illustrating the topic of this post, this is not production-ready code. I tested it for a few types 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 (i.e., in the server GetHolder / PutHolder method, in the type builder, etc.), and the type builder library only supports a fixed set of types, chosen for this sample only – in a real usage if the types would be created from some external storage such as a database (if they were fixed they would simply be written as “normal” ones instead of dynamic).
Now onto the interesting code. The DynamicTypeBuilder helper class is a singleton which knows how to build some pre-defined types. All the types are created in the same (dynamic) assembly so we can verify whether a certain type exists or not. The types are cached so that we only create a given type once.
The types supported in this sample are simple DTO types, with a few properties, and a ToString implementation which will help showing their values. Each type is created using a helper method as shown below.
CreateType is where the types are actually created. Each type is decorated with the DataContractAttribute and has fields and properties defined based on the parameters passed to the method. I’ll omit the detailed implementation of CreateProperty and OverrideToString here (they’re in the project on the code gallery), as they aren’t really related to the topic on this post.
Now onto the resolver. First: serialization (TryResolveType). When an instance of MyHolder is being serialized, if since the actual type of the MyObject property comes from the dynamic assembly (here we do a simple name comparison), we resolve that type using a constant namespace and the type name. If it doesn’t, then we delegate to the known type resolver which is passed to the method. During deserialization (ResolveName) we go the opposite way: if the namespace is our constant value, and we can load the dynamic type from our builder, then we return that type. If not, we again delegate the call to the resolver.
The infrastructure pieces are done, we can now test to see if everything works out fine. First the server: in a simple self-hosted service, we replace the resolver in all operations in the endpoint contract. Nothing fancy here.
The client starts in a similar way: define the channel factory, update the DataContractSerializerOperationBehavior to set the resolver, then call the server operations. The Echo operation is shown below.
The PutHolder operation is similar to the first one: the client sends an object which doesn’t exist on the sever, and it’s then created by the resolver at the server process. The GetHolder is interesting in that in the client the type doesn’t exist yet, and we can show by enumerating the types in the dynamic assembly. But the operation call should work out just fine as well.
And that’s it. The full code (including the methods not mentioned here) is on the code gallery sample (link to the code at the end of this post).
Unlike with the data contract surrogates, resolvers can be used to change the name / namespace of primitive types. If you have, for example, a string object assigned to a member whose declared type is object, you can safely use a resolver to change the type name / namespace used in the type hint for the string. Another thing is that using a resolver makes the serialization slower than using the “standard” known types feature (since known types are static, they can be cached and the calls don’t need to be made all the time), so be aware that when using the resolver, the additional functionality comes at a price in terms of execution time.
Wrapping up the serialization extensibility, with a short post on extensible data object.
[Code in this post]
[Back to the index]