0
votes
  1. is the following a known bug in Silverlight?
  2. if so, are there any good workarounds?

the class hierarchy is simple:

on the first PCL:

namespace ClassLibrary1
{
    [DataContract]
    public class BaseClass
    {
        [DataMember]
        public string BaseString { get; set; }
    }
}

on the second PCL (of course, referencing the first...)

namespace ClassLibrary2
{
    [DataContract]
    public class Derived : BaseClass
    {
        [DataMember]
        public string DerivedString { get; set; }
    }
}

the service (on the WebApp):

namespace SilverlightApplication1.Web
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [KnownType(typeof(Derived))]
    public class Service1
    {
        [OperationContract]
        public List<BaseClass> GetSomething()
        {
            var data = new List<BaseClass>();
            data.Add(new Derived());
            return data;
        }

    }
}

Now, the service reference does not add the ServiceKnownType attribute to the reference.cs file. and the resulting error:

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter :GetQueueItemsResult. The InnerException message was 'Element 'http://schemas.datacontract.org/2004/07/XXX_BASE_CLASS' contains data of the 'http://schemas.datacontract.org/2004/07/XXX_DERIVED_CLASS' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'DERIVED_CLASS' 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.'. Please see InnerException for more details.

[UPDATED] the error is thrown on the client, of course. the server side returns the correct values, bu the client cannot parse them properly. Fiddler says the server returned 0 bytes. but in reality it's the client who fails to deserialize the data.

I need some way to tell the runtime deserializer how to deserialize the BaseClass into the actual type transferred. [/UPDATED]

3

3 Answers

1
votes

You need to tell the DataContractSerializer to serialize the derived as the base class. To do this you need to clarify the DataContract with a Name attribute on the derived class:

[DataContract(Name="BaseClass")]
public class Derived : BaseClass 
    [DataMember]
    public string DerivedString { get; set; }
}

Im 100% not sure if you need to declare the Derived as a KnownType - I suspect strongly that you DO.

In addition you are using KnownType on the Service class - its my understanding you should use ServiceKnownType here. Generally you have a choice either:
a. Use KnownType on the object classes.
b. Use ServiceKnownType on the Service contract (usually on the Interface delcaration for the Service).
I prefer the later b. because it groups all KnownTypes in one place - where as a. has them scatered all over the code on each object - but that is just personal prefernce.

0
votes

How about setting the KnownType attributes at the DataContract type definitions, enabling the deserializer to find the correct type during runtime, does it solve the issue?

namespace ClassLibrary1
{
    [DataContract]
    [KnownType(typeof(BaseClass))]
    [KnownType(typeof(Derived))]
    public class BaseClass
    {
        [DataMember]
        public string BaseString { get; set; }
    }
}
namespace ClassLibrary2
{
    [DataContract]
    [KnownType(typeof(BaseClass))]
    [KnownType(typeof(Derived))]
    public class Derived : BaseClass
    {
        [DataMember]
        public string DerivedString { get; set; }
    }
}

To be honest, I'm not sure whether you have to set the attributes at both DataContracts or only at the derived type or only at the basetype.

0
votes

This seems to be a bug in Silverlight/client code generator.

if you have a service returning a collection of "base class" the silverlight client code generator (add service reference) will not be able to add the derived types as ServiceKnownType / KnowType or whatever on the generated client code.

the solution we are currently using (until we abandon SL altogether) is to manually copy-paste the ServiceKnownType declarations into the generated code for all derived type - every time we generate the code.

disgusting! but works.