7
votes

We are using a base class for all of our Response DTO's in our application. The class has the following signature:

[Serializable]
public abstract class ResponseBase
{
    public bool Successful { get; set; }
    public List<ResponseMessage> Messages { get; set; }

    //...Other code...
}

The Messages collection can be any of the following types:

[Serializable]
[XmlInclude(typeof(DebugMessage))]
[XmlInclude(typeof(InfoMessage))]
[XmlInclude(typeof(ValidationMessage))]
[XmlInclude(typeof(WarnMessage))]
[XmlInclude(typeof(RecoverableFaultMessage))]
[XmlInclude(typeof(FatalFaultMessage))]
public abstract class ResponseMessage
{
    //..Other code...
}

With concrete versions of:

[Serializable]
public class DebugMessage : ResponseMessage
{
    public override MessageType MessageType { get { return MessageType.Debug; } }
}
[Serializable]
public class InfoMessage : ResponseMessage
{
    public override MessageType MessageType { get { return MessageType.Info; } }
}
[Serializable]
public class ValidationMessage : ResponseMessage
{
    public override MessageType MessageType { get { return MessageType.Validation; } }
}
[Serializable]
public class WarnMessage : ResponseMessage
{
    public override MessageType MessageType { get { return MessageType.Warn; } }
}
[Serializable]
public class RecoverableFaultMessage : ResponseMessage
{
    public override MessageType MessageType { get { return MessageType.RecoverableFault; } }
}
[Serializable]
public class FatalFaultMessage : ResponseMessage
{
    public override MessageType MessageType { get { return MessageType.FatalFault; } }
}

All DTO Response objects inherit from ResponseBase however even with the following ServiceKnownTypes on the WCF Contract

[ServiceKnownType(typeof(ResponseBase))]
[ServiceKnownType(typeof(ResponseMessage))]
[ServiceKnownType(typeof(List<ResponseMessage>))]
[ServiceKnownType(typeof(DebugMessage))]
[ServiceKnownType(typeof(InfoMessage))]
[ServiceKnownType(typeof(ValidationMessage))]
[ServiceKnownType(typeof(WarnMessage))]
[ServiceKnownType(typeof(RecoverableFaultMessage))]
[ServiceKnownType(typeof(FatalFaultMessage))]
[ServiceKnownType(typeof(List<DebugMessage>))]
[ServiceKnownType(typeof(List<InfoMessage>))]
[ServiceKnownType(typeof(List<ValidationMessage>))]
[ServiceKnownType(typeof(List<WarnMessage>))]
[ServiceKnownType(typeof(List<RecoverableFaultMessage>))]
[ServiceKnownType(typeof(List<FatalFaultMessage>))]

When we load a Message into the ResponseBase Messages collection the following exception gets thrown:

Error in line 1 position 906. Element 'http://schemas.datacontract.org/2004/07/CX.Framework.Common.BaseTypes:ResponseMessage' contains data from a type that maps to the name 'http://schemas.datacontract.org/2004/07/CX.Framework.Common.BaseTypes:WarnMessage'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver or add the type corresponding to 'WarnMessage' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.

We have done everything from ServiceKnownType to XMLInclude on the derived types. I'm at a bit of a loss as to how to resolve this and would appreciate any assistance anyone can provide.

1
Try putting the known type attributes for the ResponseMessage on the ResponseBase class definition. I do similar (using [DataContract] and [KnownType]) and it requires that the class that has members of the polymorphable type be decorated with [KnownType(typeof(DerivedTypeA))].Paul Fleming
Should have posted as the answer @flem! This is what worked and was first reply on the question, bump on the comment but thank you for the answer.VulgarBinary
Pleasure... It's not just about the rep! ;)Paul Fleming

1 Answers

5
votes

A few things:

1) [XmlInclude] will have no effect on the DataContractSerializer, it's only used by the XmlSerializer.

2) As the commenter "flem" suggested, I would use [KnownType] directly on ResponseMessage instead of [ServiceKnownType] on the service.

3) I don't remember whether the DataContractSerializer even looks for [KnownType] on [Serializable] types. At least for testing purposes for now, try making your types [DataContract] instead (and attribute the data members with [DataMember]) if #2 above doesn't help.