您是不是在 WCF Web 服务中使用枚举类型?
Posted
技术标签:
【中文标题】您是不是在 WCF Web 服务中使用枚举类型?【英文标题】:Do you use enum types in your WCF web services?您是否在 WCF Web 服务中使用枚举类型? 【发布时间】:2010-09-24 11:45:37 【问题描述】:我听一些人说枚举是邪恶的,不应该在 Web 服务中使用,因为如果分配了一些值,或者如果枚举被标记为Flags 属性。他们还说,暴露枚举的 Web 服务更难维护,但不能真正给我可行的论据。那么根据您的经验,在 WCF Web 服务中使用枚举的优缺点是什么?
【问题讨论】:
【参考方案1】:人们建议在 web 服务中避免使用枚举的原因是因为它们会产生微妙的向后兼容问题。
这同样适用于常规枚举,但在 Web 服务中,问题更加明显,特别是在 .NET 生成的代理中(见下文)。
如果仅输入枚举,则没有问题。 如果 enumerate 可以是 out 参数,那么如果您添加一个新元素并将其返回,那么旧客户端可能会遇到问题: 如果客户端使用 .NET 生成的代理,它会在调用者处理它之前中断(在反序列化中) 即使为代理生成的代码支持更改(例如,如果它将枚举映射到字符串),客户端中的用户代码也可能无法正确处理新的意外值(很容易成为从未执行的路径)通过将参数定义为字符串,您可以向您的 API 用户发出该值将来可能会更改的信号。即使您认为该值永远不会改变,也是做好准备的好习惯。
Dare Obasanjo 有一个good post 讨论这个话题。
【讨论】:
作为枚举向后兼容的一种解决方法,可以将它们声明为 [DataContract] 并且仅将旧值设置为 [DataMember] ,但保留不带属性的新值。 "如果仅输入枚举,则没有问题。" 不完全正确:如果您重命名了 .NET 生成的代理使用的值之一,那么旧的当服务器无法反序列化该值时,向您发送旧名称的客户端会出现异常。【参考方案2】:我在 WCF 中使用过枚举,也在互操作性场景中使用过。 如果您控制服务的双方,则使用起来会更容易。 如果您只控制服务的一侧,则需要注意您提到的问题。
枚举比字符串变量或您可能选择使用的其他变量要好得多。 使用字符串而不是枚举是 SOA 中称为“松散的 Goosey”的反模式。
【讨论】:
【参考方案3】:在 WSDL 和 XSD 中通过 xsd:enumeration
架构元素完全支持枚举。它同时支持单个值和标志样式的枚举,其中标志枚举中的多个值用空格分隔。
因此,在任何符合标准的平台上使用枚举都应该没有问题。
【讨论】:
MMind 提到的向后兼容性是一个严重的问题,经常被忽视。【参考方案4】:当然,这完全取决于您打算在哪里使用此 WCF 服务。
如果是单个应用程序使用它,那么更改合约不会有任何影响。
如果是多个内部应用程序,更改合约可能需要对其他应用程序进行一些更改。
最后,如果 WCF 服务是公开的,您可能必须提供 2 个版本不同的服务,以便使用它们的人有时间将他们的客户端版本转移到新服务。
这完全取决于您的需求。
【讨论】:
【参考方案5】:使用除枚举之外的任何其他东西都不能解决您的兼容性问题,它只会隐藏它。 假设您使用 int 替换枚举。你是真的解决了兼容性问题还是只是伪装了它,直到客户端运行时达到未知值?
但是,有一点值得一提:WCF 代理不会重新创建显式设置的枚举数值。如果枚举是用“holes”声明的,比如
enum ErrorCodes
OK = 0,
GenericError = 100,
SomeOtherError = 101,
客户端表示将是这样的
enum ErrorCodes
OK,
GenericError,
SomeOtherError,
...在客户端导致 (int)ErrorCodes.GenericError 为 1。
你会有句法等价,但不是数字等价。
【讨论】:
在较新的 .Net 版本(例如 4.5.1)中已修复不保留“漏洞”。【参考方案6】:WSDL 中的枚举必须被视为维护问题。
添加或删除枚举值是(应该是!)接口重大更新的触发器。 如果枚举是一个输出值,那么您必然需要通过一个新的 URI 定义一个新版本的 WSDL,以防止当前客户端违反已建立的合同(“如果他们收到这些新的、意外的值作为回报怎么办? ?”) 如果枚举是输入值,您可以将其视为次要更新(“因为当前客户不需要知道这个新值”),但是,这些客户从添加这个中受益的唯一方法新选项/功能(您添加这个新枚举值是有原因的,对吗?)将要求他们稍后或更快地切换到新版本的界面。
我认为这与枚举的功能意义无关。
坚持最佳做法,您会很安全。
【讨论】:
枚举可以声明为 [DataContract] 并且只有旧值设置为 [DataMember] ,但在新的 MAJOR 版本之前保留没有属性的新值【参考方案7】:我在基于 WCF 的服务中使用了枚举,没有任何问题。您提到的可能问题绝对是要考虑的事情,但如果您确保在相当静态的情况下应用枚举,您可能不会遇到太多麻烦。
【讨论】:
【参考方案8】:真实世界的故事(值因匿名而改变)。在应用程序中使用隐式enum
public enum orange, banana, mango
围绕顺序和新值进行了一些重构,我们决定明确说明:
public enum orange=1, banana=2, grape=3, mango=4
看起来无伤大雅... 接下来,一个网站爆炸了。从头开始,检查服务,添加调试消息,一切似乎都很好。 一天后,陷入困境,原因是基础 wcf 服务使用枚举作为返回类型。
显然,Wcf 不喜欢没有 默认 值的枚举。
修复:
public enum wcfBugBane=0, orange=1, banana=2, grape=3, mango=4
所以……它可能会咬你。
【讨论】:
【参考方案9】:这是一种方法。也许它很麻烦。我真的不喜欢不能使用枚举。
它优雅地处理无法识别的值的反序列化,而是返回默认值。默认值需要是安全的——可以是可接受的回退,也可以是应用程序可以识别为异常的值。 (如“未指定”。)
扩展可以避免在比较之前进行空值检查。
[DataContract]
public class EnumValue<T> where T : struct
[DataMember]
private string _raw = string.Empty;
[IgnoreDataMember]
private bool _parsed;
[IgnoreDataMember]
private T _parsedValue;
public EnumValue()
Set(default(T));
public EnumValue(T value)
Set(value);
internal T Value
get
if (_parsed) return _parsedValue;
if (!Enum.TryParse<T>(_raw, out _parsedValue))
_parsedValue = default(T);
_parsed = true;
return _parsedValue;
public void Set(T value)
_raw = value.ToString();
_parsedValue = value;
_parsed = true;
public static class EnumValueExtensions
public static T GetValue<T>(this EnumValue<T> enumValue) where T : struct
return enumValue == null ? default(T) : enumValue.Value;
public static bool EqualsValue<T>(this EnumValue<T> enumValue, T compareTo) where T : struct
return (enumValue.GetValue().Equals(compareTo));
【讨论】:
以上是关于您是不是在 WCF Web 服务中使用枚举类型?的主要内容,如果未能解决你的问题,请参考以下文章