WCF 服务未反序列化枚举值
Enum Value Is Not Deserialized By WCF Service
我构建了一个 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.TypeA
或 Code.TypeB
时,服务总是正确反序列化它。
为了尽职调查,其他关于将枚举传递给 WCF 服务的帖子参考了 DataContract
、EnumMember(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 not deserializing value types. Mysterious behaviour
编辑 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:
我构建了另一个仅包含上述代码的 Web 服务,但它没有重现我所看到的行为。我的猜测是其他一些代码正在压缩 DataContractSerializer,或者有一些相关的 IIS/WCF 设置,或者一些未解决的数据合同问题。
我还构建了另一个连接到两个网络服务的网络客户端,它收到的结果与第一个相同。
编辑 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 序列化程序与该外部服务通信。
关键在这里:
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
XML 序列化程序用于生成代理,而不是 DataContractSerializer。您是否不小心指定了 XmlSerializer?您是否尝试使用 .asmx 服务?
一旦您找出导致使用 XmlSerializer 生成代码的原因,您就会得到答案,但从您发布的内容中并不能立即看出。
由于某些未知原因,客户端发出的 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;}
}
我构建了一个 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.TypeA
或 Code.TypeB
时,服务总是正确反序列化它。
为了尽职调查,其他关于将枚举传递给 WCF 服务的帖子参考了 DataContract
、EnumMember(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 not deserializing value types. Mysterious behaviour
编辑 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:
我构建了另一个仅包含上述代码的 Web 服务,但它没有重现我所看到的行为。我的猜测是其他一些代码正在压缩 DataContractSerializer,或者有一些相关的 IIS/WCF 设置,或者一些未解决的数据合同问题。
我还构建了另一个连接到两个网络服务的网络客户端,它收到的结果与第一个相同。
编辑 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 序列化程序与该外部服务通信。
关键在这里:
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
XML 序列化程序用于生成代理,而不是 DataContractSerializer。您是否不小心指定了 XmlSerializer?您是否尝试使用 .asmx 服务?
一旦您找出导致使用 XmlSerializer 生成代码的原因,您就会得到答案,但从您发布的内容中并不能立即看出。
由于某些未知原因,客户端发出的 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;}
}