您是不是在 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 服务中使用枚举类型?的主要内容,如果未能解决你的问题,请参考以下文章

使界面在 WCF Web 服务中可见

将单个枚举值传递给 WCF 服务时,protobuf-net 引发有关无效线路类型的异常

在 WCF 数据服务中使用枚举

为啥我必须使用 WCF 而不是 Web 服务? [复制]

Web 服务和 WCF 有啥区别?他们不是同一个东西吗?

VB6 连接到 WCF