C# 忽略SOAP反序列化中的无效枚举值

C# 忽略SOAP反序列化中的无效枚举值,c#,soap,enums,webservices-client,C#,Soap,Enums,Webservices Client,我在ASP.NET Web服务上有一个WebMethod,它返回一个枚举数组。如果添加了一个新值,并且该值由函数调用返回,那么webservice的使用者将抛出一个异常,即使它不关心该枚举值 [WebMethod] public UserRole[] GetRoles(string token) 部分wsdl: <s:simpleType name="UserRole"> <s:restriction base="s:string"> <s

我在ASP.NET Web服务上有一个WebMethod,它返回一个枚举数组。如果添加了一个新值,并且该值由函数调用返回,那么webservice的使用者将抛出一个异常,即使它不关心该枚举值

[WebMethod]
public UserRole[] GetRoles(string token)
部分wsdl:

  <s:simpleType name="UserRole">
    <s:restriction base="s:string">
      <s:enumeration value="Debug" />
      <s:enumeration value="EventEditor" />
      <s:enumeration value="InvoiceEntry" />
    </s:restriction>
  </s:simpleType>

(消费者是使用此wsdl编译的,但是wsdl会发生更改,现在允许使用新值-如果返回该值,则客户端会引发XML异常。)

是否有任何方法覆盖此类型的SOAP反序列化,以便捕获错误并从数组中删除该项或用默认值替换它?如果这是使用JSON而不是XML,我可以注册一个JsonConverter来处理该类型,所以我想我正在寻找一个类似的全局“RegisterConverter”类型函数。我认为不存在,但希望得到一些帮助

任何用属性修饰枚举的方法都不会起作用,因为所有代码都是由wsdl生成的,并在更新web引用时重新生成。通常,如果我想修改一个由wsdl生成的类,我可以创建一个分部类,但这对枚举不起作用。甚至不确定是否可以重写XmlSerialization代码,即使它是一个类



其他一些背景:

这实际上是在我尝试使用动态枚举时实现的。wsdl是从数据库查找中生成的,这样我就可以向数据库添加额外的值,并且消费应用程序将能够访问允许的值,而无需重新编译Web服务。通过这种方式,我可以通过枚举类型实现intellisense和约束强制,但可以在不紧密耦合webservice代码和客户端代码的情况下添加值。问题是,如果我添加了一个新值,它可能会破坏未使用新wsdl更新的使用者。。。我宁愿忽略这个价值,因为消费者无论如何都不知道该怎么处理它


SOAP扩展可能是解决此问题的方法(我知道如何将SOAP扩展添加到Web服务本身,但不知道如何在客户端添加一个),但这并不理想,因为我真的希望有一种通用的方法来轻松处理此问题,这样我的代码中就可以有更多的动态枚举(它们不是真正动态的,但其思想是值通过Web服务的中间层而不必重新编译该中间层)。类似于“XmlSerialization.RegisterConverter(MyDynamicEnumType,DynamicEnum.Convert)”的东西将是理想的,在这里我可以定义一个通用函数来使用和注册。)

仍然希望其他人会有答案,但我至少想出了一些比我最初使用Regex.Replace删除对我不认识的枚举值的引用更好的方法

我正在为web服务使用分部类,并覆盖GetReaderMessage,如下所示:

namespace Program.userws  //note this namespace must match the namespace 
                          //that the webservice is declared in (in auto-generated code)
{
    partial class UserWebService
    {
        protected override XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize)
        {
            return new EnumSafeXmlReader(message.Stream);
        }
    }
}
这是EnumSafeXmlReader的定义:

public class EnumSafeXmlReader : XmlTextReader
{
    private Assembly _callingAssembly;

    public EnumSafeXmlReader(Stream input) : base(input)
    {
        _callingAssembly = Assembly.GetCallingAssembly();
    }

    public override string ReadElementString()
    {
        string typename = this.Name;
        var val = base.ReadElementString();

        var possibleTypes = _callingAssembly.GetTypes().Where(t => t.Name == typename);
        Type enumType = possibleTypes.FirstOrDefault(t => t.IsEnum);

        if (enumType != null)
        {
            string[] allowedValues = Enum.GetNames(enumType);

            if (!allowedValues.Contains(val))
            {
                val = Activator.CreateInstance(enumType).ToString();
            }
        }

        return val;
    }
}
我还为UserRole-
UserRole.Unknown添加了一个新值,并确保它是允许值列表中的第一个值

<s:simpleType name="AcctUserRole">
  <s:restriction base="s:string">
    <s:enumeration value="Unknown"/>
    <s:enumeration value="Debug"/>
    <s:enumeration value="EventEditor"/>
    <s:enumeration value="InvoiceEntry"/>
  </s:restriction>
</s:simpleType>
这将导致一个
UserRole[]
包含
UserRole.InvoiceEntry
UserRole.Unknown

但如果我有UserRole类型的字段或属性:

<User>
    <ID>5</ID>
    <Name>Zorak</Name>
    <PrimaryRole>UnexpectedRole</PrimaryRole>  <!-- causes an exception -->
</User>

5.
佐拉克
意外孔
这仍然会失败,因为读者无法知道“PrimaryRole”需要反序列化以键入UserRole。XmlSerializer知道这一点,但据我所知,没有办法覆盖XmlSerializer,只有XmlReader

我想给EnumSafeXml读取器提供足够的信息来识别将反序列化为enum类型的标记并非完全不可能,但这比我现在愿意去做的要麻烦得多——我特别需要它在“enum值数组”的情况下工作,现在它就这样做了

我确实在类型上添加了一些缓存,这样我只需要检查一次标记名,看看它是否也是枚举的名称,但为了清晰起见,在本例中我删除了它



我欢迎任何其他可能的解决方案或建议来改进此解决方案。

我将冒被否决的风险,声明一个显而易见的事实:不要在服务合同中使用枚举。正如您所确定的,除了在固定域中,它们是易碎的

如果我是该服务的消费者,
Unknown
条目会让我问“当该服务返回该服务时,我该怎么办?”你会回答“别担心,它不会在那里,只是为了客户端兼容性”,我会回答“如果该服务不返回它,它在合同中做什么?”


返回一个
字符串[]
,并让您的客户端解析数组以获取它可以处理的信息。如果intellisense真的是您的目标,那么您可以在客户机中定义枚举的一个子集,并且您可以在搜索更复杂的解决方案的时间内实现这一点上百次。步走开。从…起这个枚举。

嗯,我想到了两种可能的解决方案。一种是覆盖WebService声明上的GetReaderformMessage(在部分类中),然后对枚举名称和值进行正则表达式匹配,另一种是向上发送可接受值的列表,并让WebService拒绝发送不在该列表中的值。这两个问题都很糟糕,我希望有人能想出比我目前得到的更好的答案。如果没有,我将在明天发布我自己的janky答案。好吧,我确实最终覆盖了GetReaderformMessage,但在枚举名称的整个消息字符串上比正则表达式匹配稍好一些。。。仍然希望有一种更好的方法来覆盖枚举的反序列化。WSDL和XSD的全部要点是在服务和客户机以及y之间建立一个契约
<User>
    <ID>5</ID>
    <Name>Zorak</Name>
    <PrimaryRole>UnexpectedRole</PrimaryRole>  <!-- causes an exception -->
</User>