您是否在WCF Web服务中使用枚举类型?

时间:2008-11-28 18:03:32

标签: wcf enums

我听说有些人说枚举是邪恶的,不应该在Web服务中使用,因为如果分配了一些值,或者如果枚举标记了枚举,服务器和客户端之间可能会出现不匹配Flags属性。他们还说,暴露枚举的网络服务难以维护,但实际上并没有给我可行的论据。那么根据您的经验,在WCF Web服务中使用枚举的优缺点是什么?

9 个答案:

答案 0 :(得分:41)

人们建议避免在网络服务中使用枚举的原因是因为它们会产生微妙的向后兼容问题。

这同样适用于常规枚举,但在Web服务中,问题在.NET生成的代理中更为明显(见下文)。

  • 如果只输入枚举,则表示没有问题。
  • 如果枚举可以是out参数,那么如果添加新元素并将其返回,则旧客户端可能会出现问题:
    • 如果客户端使用的是.NET生成的代理,它将在调用者处理它之前中断(在反序列化中)
    • 即使生成的代理代码支持更改(例如,如果它将枚举映射到字符串),客户端中的用户代码也可能无法正确处理新的意外值(它可能很容易成为永不执行的路径)

通过将参数定义为字符串,您可以向API用户表明该值可能在将来发生变化。即使你认为价值永远不会改变,也是一个好的做法。

Dare Obasanjo就此主题有一个good post

答案 1 :(得分:17)

我在WCF中使用了枚举,也在互操作方案中使用了枚举。 如果您控制服务的两面,则更容易使用。 如果您只控制服务的一方,则需要注意您提到的问题。

枚举比字符串变量要好得多,或者你可以选择使用什么。 使用字符串而不是枚举是SOA中称为“松散的Goosey”的反模式。

答案 2 :(得分:10)

WSDL和XSD通过xsd:enumeration架构元素完全支持枚举。它提供对单值和标志样式枚举的支持,其中flags枚举中的多个值由空格分隔。

因此,对于任何符合标准的平台使用枚举应该没有问题。

答案 3 :(得分:3)

当然,这一切都取决于您将使用此WCF服务的位置。

如果是使用它的单个应用程序,那么更改合同将不会产生任何影响。

如果是多个内部应用程序,更改合同可能需要对其他应用程序进行一些更改。

最后,如果WCF服务是公共的,您可能必须提供具有不同版本的服务的2个版本,以便使用它们的人有时间将他们的客户端版本转移到新服务。

这完全取决于你的需要。

答案 4 :(得分:2)

WSDL中的枚举必须被视为维护的关注点。

添加或删除枚举值(应该是!)是接口主要更新的触发器。 如果枚举是一个输出值,那么你一定需要通过一个新的URI定义一个新版本的WSDL,这样就可以防止当前客户违反已建立的合同(“如果他们收到这些新的,意外的值,那将会是什么回报?“) 如果枚举是输入值,您可以将其视为次要更新(“因为当前客户端不需要知道这个新值”),但是,这些客户只能通过添加此更新来获益新选项/ functionnality(你添加了这个新的枚举值,原因,对吗?)是要求他们稍后或更早地切换到新版本的界面。

这与enum的功能意义无关,我认为。

保持最佳实践方面,你会安全。

答案 5 :(得分:2)

使用除枚举之外的任何其他内容并不能解决您的兼容性问题,它只会隐藏它。 假设您使用int来替换枚举。您是否真的解决了兼容性问题,或者只是在客户端运行时遇到未知值之前伪装它?

但是,值得一提的是:WCF代理不会重新创建显式设置的枚举数值。如果enum声明为“holes”,如

enum ErrorCodes
{
  OK = 0,
  GenericError = 100,
  SomeOtherError = 101,
}

客户端表示将是这样的

enum ErrorCodes
{
  OK,
  GenericError,
  SomeOtherError,
}

...在客户端上导致(int)ErrorCodes.GenericError为1。

你将具有句法等价,但不具有数字等价。

答案 6 :(得分:1)

我在基于WCF的服务中使用了枚举而没有任何问题。您提到的可能问题绝对值得考虑,但如果您确保在相当静态的情况下应用枚举,则可能不会有太多麻烦。

答案 7 :(得分:0)

这是一种方法。也许这很麻烦。我真的不喜欢不能使用枚举。

它可以优雅地处理无法识别的值的反序列化,而是返回默认值。默认值需要是安全的 - 要么是可接受的回退,要么应用程序可以识别为异常。 (比如"未指定。")

扩展可以防止在比较之前进行空值检查。

[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));
    }
}

答案 8 :(得分:0)

真实世界的故事(因匿名而改变了价值观)。用于在应用程序中使用隐式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 }

所以......它可能会咬你。