I'm new to WCF and was hoping someone could offer assistance. I've got a non-abstract base class that has [knowntype]
applied to it with the derived classes in, and this is used in the body of the request to my service in an array. When I call the service using XML with the derived class in, all I get in code after deserialization is the base class, not the derived class.
The input XML has an xml instance type (xsi:type
) attribute that appears to be completely ignored - whatever I set it to, the system just gives me the base class with no error.
The actual code/etc I'm getting this with is huge. So, I've knocked up a representative test, but this works properly, and interestingly, if I change the xsi:type
to an invalid type, I get an exception from the DataContractSerializer
, unlike with the code I want to work!
If I change the name of the element to force an exception, I see mention of the DataContractSerializer
:
`Start element 'itemz' does not match end element 'item'. Line 110, position 23.
at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)
at System.Xml.XmlUTF8TextReader.ReadEndElement()
at System.Xml.XmlUTF8TextReader.Read()
at System.Xml.XmlBaseReader.Skip()
at System.Runtime.Serialization.XmlReaderDelegator.Skip()
at ReadArrayOfitemFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString , XmlDictionaryString , CollectionDataContract )
at System.Runtime.Serialization.CollectionDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDe legator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDe legator xmlReader, Int32 id, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
at ReadinteropSectionFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
...
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)`
So, I'm a bit stumped: judging by the exceptions, it looks like it's using the DataContractSerializer in both cases, but in the real code, it's not even trying to use the derived type. Unlike the older ASMX style services, I have not yet found a way of jumping into the serializer code to see what on earth it is doing. What am I doing wrong, and how can I debug the serializer?
If it's any help, I can try to provide more code/xml. The 'demo' code I've got is below; just like the real code, it uses a [MessageContract]
for the service, but the entities are all DataContract'd to the hilt (in the real code, SVCUTIL generated the proxy code with similar attributes).
[DataContract] public class Security
{
[DataMember] public string Username { get; set; }
}
[DataContract] public class Wrapper
{
[DataMember] public BaseEntity[] MyEntities { get; set; }
}
[DataContract, KnownType(typeof(AdvancedEntity))] public class BaseEntity
{
[DataMember] public string Name { get; set; }
}
[DataContract] public class AdvancedEntity : BaseEntity
{
[DataMember] public int Age { get; set; }
}
[ServiceContract] interface ITrivialService
{
[OperationContract] Response DoStuff(Request request);
}
[MessageContract] public class Request
{
[MessageHeader] public Security AuthenticationDetails { get; set; }
[MessageBodyMember] public Wrapper MyWrapper { get; set; }
}
[MessageContract] public class Response
{
[MessageBodyMember] public BaseEntity Entity { get; set; }
}
public class TrivialService : ITrivialService
{
public Response DoStuff(Request request)
{
string newName = string.Empty;
BaseEntity entity = request.MyWrapper.MyEntities[0];
//do something different depending on which entity we got
AdvancedEntity advancedEntity = entity as AdvancedEntity;
if (advancedEntity != null)
{
advancedEntity.Name = request.AuthenticationDetails.Username;
advancedEntity.Age++;
}
else
{
entity.Name = entity.Name + " and " + request.AuthenticationDetails.Username;
}
Response response = new Response();
response.Entity = entity;
return response;
}
}