# Friday, June 15, 2007

This may have been understood by the set of all people not me, but I didn't realize that the KnownType attribute doesn't seem to work on a MessageContract.  The KnownType attribute is supposed to tell the data contract serializer that there might be some extra types it needs to know about when serializing your objects.  It's equivalent to the XmlInclude attribute used by the XmlSerializer.  If I have some code that looks like this...

[MessageContract]

public class UserRequest

{

    [MessageBodyMember]

    public string UserName;

    [MessageBodyMember]

    public bool SendExtended = false;

}

 

[MessageContract]

public class UserResponse

{

    [MessageBodyMember]

    public string Message;

    [MessageBodyMember]

    public UserBase User;

}

 

[DataContract]

public class UserBase

{

    [DataMember]

    public string FirstName;

    [DataMember]

    public string LastName;

}

 

[DataContract]

public class BankUser : UserBase

{

    public BankUser() : base() { }

 

    [DataMember]

    public int TupperwarePoints;

 

}

In my server side implementation, if the "SendExtended" flag is true in the UserRequest, the object returned in the "User" property of the UserResponse is a BankUser, not a UserBase object.  If the code runs like this, that fails horribly, since the data contract serializer is set to serialize a UserBase, not a BankUser. 

I would have thought that I could mark up the UserResponse, like so

[MessageContract]

[KnownType(typeof(BankUser))]

public class UserResponse

{

    [MessageBodyMember]

    public string Message;

    [MessageBodyMember]

    public UserBase User;

}

Turns out this totally doesn't work.  I get the same serializer error, despite the KnownType attribute.  If I make UserResponse a DataContract instead of a MessageContract, it works fine, and the appropriate BankUser object is received on the client side. 

However, in my actual code, I need the MessageContract.  The alternative is marking up the UserBase class itself, like this:

[DataContract]

[KnownType(typeof(BankUser))]

public class UserBase

{

    [DataMember]

    public string FirstName;

    [DataMember]

    public string LastName;

}

This works just fine, but from a design standpoint it's teh suck.  This means that I have to tell my base class about all it's derived types, thus defeating the entire point of inheritance.  That's why I never used the XmlInclude attribute.  I might have to rethink how the messages are composed and see if there's a better way to encapsulate things.

On a slightly unrelated note, it seems that my ServiceHost takes much longer to start up when the Request and Response are defined as MessageContract than when they are straight DataContracts.  I haven't tested this extensively, so it's anecdotal, but interesting nonetheless. 

Indigo | Work
Friday, June 15, 2007 11:51:12 AM (Pacific Daylight Time, UTC-07:00)
This may ring hollow, but it sorta makes sense if you look at the bigger picture- MessageContractAttribute isn't really defined as a serialization-specific attribute (it's part of the messaging subsystem in System.ServiceModel), where DataContractAttribute (and the related KnownTypeAttribute) are specific to DataContractSerializer in System.Runtime.Serialization. My MessageContract-decorated class might be serialized by any number of different serializers.

On the KnownTypes mess, you could also do the polymorphism just below the message level (as you've described), but do the KnownTypes mapping in config (declaredTypes element under system.runtime.serialization/dataContractSerializer). Not sure if that's better or worse, but if you're doing customer-specific messaging that's built above your base pipeline, you can at least extend the list of known types without having to rebuild the base assembly...
Friday, June 15, 2007 12:34:04 PM (Pacific Daylight Time, UTC-07:00)
I agree. When I stopped to reflect, it makes sense that MessageContract would be treated differently, for the reasons you suggest. I just wasn't expecting it. Polymorphism below the message level also makes sense, and I considered that, but for historical compatibility reasons that's less attractive. I think I've come up with a better solution which I'm going to blog momentarily.
Comments are closed.