枚举值未被WCF服务反序列化

8

我建立了一个WCF服务,其中有一个部分看起来像这样:

[ServiceContract]
public class Service {
    [OperationContract]
    public SomethingElse[] Method(Code a, params Something[] b) { ... }
}

[DataContract]
public class Something {
    [DataMember]
    public string Stuff {get;set;}
    [DataMember]
    public Status MyStatus {get;set;}
    public string ServerSideField {get;set;}
}

[DataContract]
public class SomethingElse {
    [DataMember]
    public Status MyStatus {get;set;}
}

[DataContract]
public enum Status {
    [EnumMember] WorksFine,
    [EnumMember] NotWorking
}

[DataContract]
public enum Code {
    [EnumMember] TypeA,
    [EnumMember] TypeB
}

现在我将其用作C#客户端的服务引用。由于某种原因,每当我调用Method时,b参数中的MyStatus属性总是设置为WorksFine,即使我将其设置为NotWorking也是如此。另一方面,无论我将a参数设置为Code.TypeACode.TypeB,该服务始终能够正确反序列化。

为了尽职尽责,关于向WCF服务传递枚举的其他帖子提到了DataContractEnumMember(Value="TypeA")ServiceKnownType等内容,因此我尝试了所有这些方法。然而,即使我使用了ServiceKnownType(如下所示),我仍然遇到同样的问题。

[ServiceContract]
[ServiceKnownType(typeof(Something)]
[ServiceKnownType(typeof(Status)]
public class Service {
    [OperationContract]
    public SomethingElse[] Method(Code a, params Something[] b) { ... }
}

对于如此基础的问题,这个问题似乎异常模糊。我也测试了从服务中传回 Status.NotWorking 并且客户端能够看到它,所以这似乎是一个单向问题。有什么建议吗?

编辑1:

类似的问题:WCF不反序列化值类型,行为神秘

编辑2:

鉴于缺乏立即响应,我将包含更多信息以便其中一些信息 能够粘在上面。

  • 我在 .NET 4.5 和 4.0 上都遇到了这个问题。
  • 该服务托管在IIS上,具有SSL和自定义身份验证方案。
  • Method 上还有一个 FaultContract 属性,但我将其排除在外,以使示例更简单。
  • 事件查看器什么也没有。IIS日志也是如此。
  • 在 Reference.cs 中的自动生成服务引用代码如下:

枚举:

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schemas.datacontract.org/2004/07/Service")]
public enum Status{ TypeA, TypeB }

方法:
// CODEGEN: Parameter 'MethodResult' requires additional schema information that cannot be captured using the parameter mode. The specific attribute is 'System.Xml.Serialization.XmlArrayAttribute'.
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Service/Method", ReplyAction="http://tempuri.org/Service/MethodResponse")]
    [System.ServiceModel.FaultContractAttribute(typeof(MyClientProject.Service.MyFault), Action="http://tempuri.org/Service/MethodMyFaultFault", Name="MyFault", Namespace="http://schemas.datacontract.org/2004/07/Service.MyFault")]
    [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
    MyClientProject.Service.Method Method(MyClientProject.Service.MethodRequest request);
编辑3:

我创建了另一个仅包含上述代码的网络服务,但它并没有重现我所看到的行为。我的猜测是可能有其他代码导致DataContractSerializer出现问题,或者存在一些相关的IIS/WCF设置,或者存在一些未解决的数据协定问题。

我还建立了另一个连接到两个网络服务的Web客户端,它接收到与第一个相同的结果。

编辑4:

使用Fiddler拦截请求,它的内容如下:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Method xmlns="http://tempuri.org/">
<a>TypeA</a>
<b><Something xmlns="http://schemas.datacontract.org/2004/07/TestService.Something">
   <Stuff>mystuffvalue</Stuff>
</Something></b>
</Method>
</s:Body>
</s:Envelope>

所以枚举值根本没有被传递!我该如何解决这个合同不匹配的问题?

编辑5

忘记提到这个 Web 服务引用了一个 ASMX 服务,并且自身使用 XML 序列化器与该外部服务进行通信。


你尝试过将数据类通过DataContractSerializer运行以查看实际序列化了什么吗?另外,你尝试过完全省略枚举中的属性吗? - Erik
我从未使用过DataContractSerializer,也没有尝试过从枚举中省略属性。我会尝试两种方法。 - stephen
好的,我现在尝试使用[OnDeserializing]来查看Something反序列化时的状态。但是MyStatus属性仍然默认为其默认值。有没有更有用的方法来查看反序列化? - stephen
同时,移除枚举上的属性会使得传入所需参数有点困难。 - stephen
1
尝试使用BasicHttpBinding,并使用Fiddler或类似工具捕获请求。然后,调整请求格式并再次将其发送到服务。这很可能是合同不匹配类型的问题。 - Kirk Broadhurst
@KirkBroadhurst 更新了。看起来请求不完整。 - stephen
2个回答

2

由于某些未知原因,客户端发出的SOAP请求省略了我需要的枚举值,因此服务器将这些枚举序列化为它们的默认值(列表中第一个枚举定义)。

我通过使省略参数成为请求正文的必需项来解决了这个问题。这是修复它的代码:

[DataContract]
public class Something {
    [DataMember]
    public string Stuff {get;set;}
    [DataMember(IsRequired=true)] // just this 1 simple change!
    public Status MyStatus {get;set;}
    public string ServerSideField {get;set;}
}

1
这似乎是一个asmx服务。你应该使用服务引用。 - moarboilerplate
我确实这样做了。但是,在Web服务的实际实现中,Web服务引用了一个ASMX Web服务。也许它不知何故泄漏出去了? - stephen
我看了你的另一篇帖子。XmlElement属性强制.NET使用XML序列化器。如果你把它们移除,你的枚举将开始工作。你在这里发布的是一个狡猾的解决方法。 - moarboilerplate
太好了,我刚刚查看了我的 Web 服务中的 ASMX Web 服务,并且在 reference.cs 中有各种 XmlElement 属性。下次我不会忘记提到 asmx 引用 :) - stephen
2
如果公共合同不包括任何XML序列化器特定的指令,比如那些属性,那么我相当确定DataContractSerializer仍然可以使用。如果不能使用,则是配置问题或者您的数据类型结构以一种DataContractSerializer不支持的方式构建。 - moarboilerplate
显示剩余2条评论

2

关键在这里:

[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]

使用XML序列化程序生成代理,而不是DataContractSerializer。您是否意外指定了XmlSerializer?您是否正在尝试使用.asmx服务?

一旦确定代码是使用XmlSerializer生成的原因,您将得到答案,但从您发布的内容中无法立即看出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接