你在你的WCF Web服务中使用枚举types吗?
我听到一些人说,枚举是邪恶的,不应该在Web服务中使用,因为如果分配了一些值,或者如果枚举被标记为Flags属性,则服务器和客户端之间可能会出现不匹配。 他们还表示,暴露枚举的Web服务更难维护,但不能真正给我可行的论据。 所以根据你的经验,在WCF Web服务中使用枚举有什么优缺点?
人们推荐避免在Web服务中枚举的原因是因为它们创build了微妙的向后兼容问题。
这同样适用于常规枚举,但是在Web服务中,问题在.NET生成的代理中更为明确(请参见下文)。
- 如果枚举是input只有你没有问题。
- 如果枚举可以是out参数,那么如果添加一个新元素并返回它,则旧客户端可能会遇到问题:
- 如果客户端使用.NET生成的代理,它将在调用者处理它之前中断(在反序列化中)
- 即使为代理生成的代码支持更改(例如,如果它将枚举映射到string)客户端中的用户代码可能无法正确处理新的意外值(它可能很容易是从未执行的path)
通过将参数定义为string,您可以向API的用户发出信号,表示该值可能在将来发生变化。 即使你认为价值永远不会改变,也是一个很好的做法。
Dare Obasanjo在这个话题上有一个很好的post 。
我已经在WCF中使用枚举,也在互操作性场景中使用。 如果您控制服务的双方,这是更容易处理。 如果您只控制服务的一个方面,则需要注意您提到的问题。
枚举是更好的stringvariables,或者你可能会select使用什么。 使用string而不是枚举是在SOA中称为“松散的Goosey”的反模式。
枚举在WSDL和XSD中通过xsd:enumeration
模式元素完全受支持。 它提供对单值和标志样式枚举的支持,其中枚举枚举中的多个值由空格分隔。
所以你应该没有问题使用枚举与任何符合标准的平台。
当然,这一切都取决于你要使用这个WCF服务的地方。
如果是单个应用程序将使用它,那么更改合同将不会有任何影响。
如果是多个内部应用程序,更改合同可能需要对其他应用程序进行一些更改。
最后,如果WCF服务是公开的,你可能需要提供不同版本的服务的2个版本,这样消费者才有时间将他们的客户端版本转移到新的服务上。
这一切都取决于你的需求,诚实。
WSDL中的枚举必须被视为维护的关注点。
添加或删除枚举值是(应该是)接口主要更新的触发器。 如果枚举是一个输出值,那么你就需要通过一个新的URI定义一个WSDL的新版本,这样可以防止当前的客户端违反已经build立的合同(“如果他们收到这些新的, ?“)如果枚举是一个input值,你可以认为这是一个小的更新(”因为当前客户不需要知道这个新的价值“),但是,那么这些客户的唯一途径是从添加这个新的选项/function(你添加了这个新的枚举值的原因,对吧?)将要求他们晚点或早点切换到新版本的界面。
这并不是与枚举的函数意义有关,我想。
留在最佳实践方面,你会安全的。
使用除枚举之外的任何内容都不能解决您的兼容性问题,只能隐藏它。 假设您使用int来replace枚举。 你真的解决了兼容性问题,或者直到客户端运行时遇到未知值时才伪装它?
不过,有一点值得一提:WCF代理不会重新显式设置枚举数值。 如果枚举声明为“孔”,如
enum ErrorCodes { OK = 0, GenericError = 100, SomeOtherError = 101, }
客户端代表将是这样的
enum ErrorCodes { OK, GenericError, SomeOtherError, }
…在客户端结果(int)ErrorCodes.GenericError是1。
你会有句法等价,但不是数字等价。
我在我的基于WCF的服务中使用枚举没有任何问题。 您提到的可能的问题绝对是需要考虑的事情,但是如果您确定在相当静态的情况下应用枚举,则可能不会有太多麻烦。
这是一个方法。 也许这很麻烦。 我真的不喜欢不能使用枚举。
它优雅地处理无法识别的值的反序列化,而是返回默认值。 默认值需要安全 – 可接受的后备或应用程序可以识别为例外的东西。 (如“未指定”。)
扩展防止在比较之前进行空检查。
[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)); } }