简体   繁体   中英

Silverlight WCF: consuming a collection of derived objects as a collection of base class results in a NetDispatcherFaultException

  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]

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM