Silverlight WCF:将派生对象的集合作为基础 class 的集合使用会导致 NetDispatcherFaultException

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

  1. 以下是 Silverlight 中的已知错误吗?
  2. 如果是这样,有什么好的解决方法吗?

class 层次结构很简单:

第一个 PCL:

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

第二个PCL(当然是参考第一个...)

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

服务(在 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;
        }

    }
}

现在, 服务引用不会将 ServiceKnownType 属性添加到 reference.cs 文件。以及由此产生的错误:

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.

[更新] 当然,错误是在客户端抛出的。服务器端 returns 正确的值,但客户端无法正确解析它们。 Fiddler 说服务器返回了 0 个字节。 但实际上是客户端未能反序列化数据。

我需要一些方法来告诉运行时反序列化程序如何将 BaseClass 反序列化为实际传输的类型。 [/更新]

如何在 DataContract 类型定义处设置 KnownType 属性,使反序列化器能够在运行时找到正确的类型,是否解决了问题?

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; }
    }
}

老实说,我不确定您是必须在两个 DataContracts 上设置属性,还是只在派生类型或基类型上设置属性。

您需要告诉 DataContractSerializer 将派生序列化为基 class。为此,您需要在派生的 class:

上使用 Name 属性阐明 DataContract
[DataContract(Name="BaseClass")]
public class Derived : BaseClass 
    [DataMember]
    public string DerivedString { get; set; }
}

我 100% 不确定您是否需要将 Derived 声明为 KnownType - 我强烈怀疑您需要声明。

此外,您还在服务上使用 KnownType class - 据我了解,您应该在此处使用 ServiceKnownType。通常您可以选择:
一种。在对象上使用 KnownType classes.
b.在服务契约上使用 ServiceKnownType(通常在服务的接口声明上)。
我更喜欢后面的b。因为它将所有 KnownTypes 分组在一个地方 - 其中作为 a.让它们分散在每个对象的代码中 - 但这只是个人偏好。

这似乎是 Silverlight/client 代码生成器中的错误。

如果您有返回 "base class" 集合的服务,则 silverlight 客户端代码生成器(添加服务引用)将无法将派生类型添加为 ServiceKnownType / KnowType 或生成的客户端代码中的任何内容。

我们目前使用的解决方案(直到我们完全放弃 SL)是手动将 ServiceKnownType 声明复制粘贴到所有派生类型的生成代码中 - 每次我们生成代码时。

恶心!但有效。