The Known Type Provider

7 commentsWritten on July 21st, 2008 by
Categories: WCF

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 :)

  • Pingback: Dew Drop - July 22, 2008 | Alvin Ashcraft's Morning Dew

  • Pingback: The Inquisitive Coder - Davy Brion’s Blog » Blog Archive » The Request/Response Service Layer

  • http://csharpie.blogspot.com Daniel

    I was running into the same annoying problem when I was trying to serialize POCOs in my pub-sub implementation.

  • Daniel

    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.

  • http://davybrion.com Davy Brion

    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

  • Tiju John

    Hi,

    for the context setting, I am exchanging messages between my nServiceBus client and nSerivceBus server. its is the namespace xyz.Messages and and a class, Message : IMessage

    I have more messages that are in the other dlls, like xyz.Messages.Domain1, xyz.Messages.Domain2, xyz.Messages.Domain3.
    and messages that derive form that base message, Message.

    I have the endpoints defined as like :

    at client

    at Server

    and the bus initialized as

    IBus serviceBus = Configure.With()
    .SpringBuilder()
    .XmlSerializer()
    .MsmqTransport()
    .UnicastBus()
    .LoadMessageHandlers()
    .CreateBus()
    .Start();

    now when i try sending instance of Message type or any type derived types of Message, it successfully sends the message over and at the server, i get the proper type.

    eg.

    Message message= new Message();
    Bus.Send(message); // works fine, transfers Message type
    message = new MessageDerived1();
    Bus.Send(message); // works fine, transfers MessageDerived1 type
    message = new MessageDerived2();
    Bus.Send(message); // works fine, transfers MessageDerived2 type

    My problem arises when any type, say MessageDerived1, contains a member variable of type Message, and when i assign it to a derived type, the type is not properly transferred over the wire. It transfers only as Message type, not the derived type.

    public class MessageDerived2 : Message
    {
    public Message;
    }

    MessageDerived2 messageDerived2= new MessageDerived2();
    messageDerived2.Message = new MessageDerived1();
    message = messageDerived2;
    Bus.Send(message); // incorrect behaviour, transfers MessageDerived2 correctly, but looses type of MessageDerived2.Message (it deserializes as Message type, instead of MessageDerived1)

    any help is strongly appreciated.

    Thanks
    TJ

  • http://davybrion.com Davy Brion

    @Tiju

    you should probably contact the NServiceBus group for this…