The Known Type Provider
Posted by Davy Brion on July 21st, 2008
Manually dealing with KnownTypes in WCF is a bit of a pain. Well, at least if you have more than a few derived types you want your WCF services to serialize/deserialize. Luckily for us, there is a way to do this automatically. Using the ServiceKnownType attribute you can specify a class and a static method of that class which will provide the Known Types. An example of this can be found on my ever-recurring Process service method:
[OperationContract]
[ServiceKnownType("GetKnownTypes", typeof(KnownTypeProvider))]
Response[] Process(params Request[] requests);
This works, but i don’t want to write a class every time i want to specify some known types of a base type in a service method. So i modified that KnownTypeProvider class so that we can now do the following:
KnownTypeProvider.RegisterDerivedTypesOf<Request>(mySharedAssembly);
KnownTypeProvider.RegisterDerivedTypesOf<Response>(mySharedAssembly);
This registers each derived type of Request and Response in the mySharedAssembly reference (of type Assembly) with the KnownTypeProvider. You can register as many known types for as many base types as you want. Obviously, you have to do this before you start hosting your service. You also need to do this client-side, before you start using your service proxies.
You’re probably thinking “wait, won’t that return all registered known types for every service call where you use this stuff?”. That wouldn’t be good, so i took care of that. The ServiceKnownType attribute of WCF requires your class to implement a method which receives a parameter of type ICustomAttributeProvider and which returns a list of types. The ICustomAttributeProvider parameter is actually a MethodInfo instance at runtime. This makes it possible to inspect the current service method’s parameters and return type. That information makes it possible to only return the KnownTypes that are relevant to the current service call. All of this sounds expensive with regards to performance, so obviously this is all cached… so you only take the performance hit on the very first request of each service method where you use this.
The KnownTypeProvider makes it extremely easy to register your KnownTypes in a generic way, can be reused across multiple service methods and it only returns the relevant KnownTypes for each service method. And it does so with a minimum of performance overhead.
You can find the code in my public svn repository as a part of my utility library (the tests can be found here). At this moment it is one of the only classes in the library, because i still have to add some other stuff before i’ll put a ‘proper release’ online
July 22nd, 2008 at 1:59 pm
[...] The Known Type Provider (Davy Brion) [...]
July 27th, 2008 at 1:07 pm
[...] The Known Type Provider [...]
August 1st, 2008 at 2:58 am
I was running into the same annoying problem when I was trying to serialize POCOs in my pub-sub implementation.
August 17th, 2008 at 6:53 am
This seems like it would provide a lot of flexibility since we wouldn’t have to declare known types explicitly, but will this method work over layers? If I use the hard-coded attribute style for my service contract, I can access those known types as proxies on the client. Since the GetKnownTypes method is static, if I put this functionality up in the core library, I can’t override it in the service layer and provide my known types there. I haven’t had much success getting the proxies when updating my service reference in client code. Is it possible to use this in a Core library scenario or should I just suck it up and implement it in the services layer specifically? It’d be nice to package it up where my implementing layers would automatically add the known types for all messages, message handlers, DTOs, et al. for free just by using the base service contract in the core, and these would be available as proxies through the service reference.
August 17th, 2008 at 7:46 pm
I actually have the KnownTypeProvider in a library (Brion.Library) which is referenced by my application.
In my application code, before i start hosting the service i do the following:
KnownTypeProvider.RegisterDerivedTypesOf<Request>(sharedAssembly);
KnownTypeProvider.RegisterDerivedTypesOf<Response>(sharedAssembly);
If i start hosting the service and i try to generate a proxy with SvcUtil, all of the known types in my application's library are present in the generated file
i haven't tried using those proxies at runtime though, since i put my known types in a shared assembly and use it on both sides. The service's metadata properly returns the known types though, so i guess it should work
Btw, you need to register the known types with the KnownTypeProvider client-side as well, at least if you're using the shared assembly approach. Not sure if you still have to do this when you're using generated proxies, but it's probably worth trying if you haven't done so already