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

Posted

技术标签:

【中文标题】Silverlight WCF:将派生对象的集合作为基类的集合使用会导致 NetDispatcherFaultException【英文标题】:Silverlight WCF: consuming a collection of derived objects as a collection of base class results in a NetDispatcherFaultException 【发布时间】:2015-11-12 18:21:03 【问题描述】:
    以下是 Silverlight 中的已知错误吗? 如果是这样,有什么好的解决方法吗?

类层次结构很简单:

在第一个 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 文件。以及由此产生的错误:

格式化程序在尝试反序列化消息时抛出异常:尝试反序列化参数时出错:GetQueueItemsResult。 InnerException 消息是'元素'http://schemas.datacontract.org/2004/07/XXX_BASE_CLASS'包含'http://schemas.datacontract.org/2004/07/XXX_DERIVED_CLASS'数据协定的数据。反序列化器不知道映射到该合约的任何类型。将与 'DERIVED_CLASS' 对应的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将其添加到传递给 DataContractSerializer 的已知类型列表中。有关详细信息,请参阅 InnerException。

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

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

【问题讨论】:

【参考方案1】:

您需要告诉 DataContractSerializer 将派生的序列化为基类。为此,您需要使用派生类上的 Name 属性来阐明 DataContract:

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

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

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

【讨论】:

序列化似乎没问题。是反序列化失败...查看对问题的编辑 以上假设客户端只需要 Base 对象。如果您想要 Derived 对象客户端,则只需序列化为 Derived 但不要将任何 [DataMemeber] 添加到 Derived(即所有状态都在 Base 中)。实际上,它将仅序列化基本部分,但反序列化为 Derived。 是的,我想我在传输它时并不清楚:(【参考方案2】:

如何在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 上设置属性,或者仅在派生类型或仅在基类型上设置属性。

【讨论】:

仅此而已。他实际上需要告诉 DataContractResolve 使用什么契约——即使用派生类的基类契约。 @Ricibob 基本上是正确的。当然我不能在基类上使用 KnownType 属性(愚蠢的 MS 认为这是一个正常的场景不是吗?)【参考方案3】:

这似乎是 Silverlight/客户端代码生成器中的一个错误。

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

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

恶心!但有效。

【讨论】:

以上是关于Silverlight WCF:将派生对象的集合作为基类的集合使用会导致 NetDispatcherFaultException的主要内容,如果未能解决你的问题,请参考以下文章

我是不是必须使用 WCF 对 silverlight 应用程序中的对象进行序列化/反序列化?

如何使用 WCF 调用从客户端 windows phone 8.0 silverlight 返回对象的方法

silverlight 应用程序不能使用 wcf 命名空间

Silverlight WCF 超时

从 Silverlight 到 WCF 的图像文件

silverlight wcf mvvm