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

类层次结构很简单:

在第一个 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 反序列化为实际传输的类型。 [/更新]

【问题讨论】:

    标签: c# wcf silverlight datacontractserializer


    【解决方案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 声明复制粘贴到所有派生类型的生成代码中 - 每次我们生成代码时。

    恶心!但有效。

    【讨论】:

      猜你喜欢
      • 2018-04-02
      • 2019-12-05
      • 1970-01-01
      • 1970-01-01
      • 2013-02-25
      • 1970-01-01
      • 2013-01-04
      • 2011-01-02
      • 1970-01-01
      相关资源
      最近更新 更多