C# 忽略SOAP反序列化中的无效枚举值
我在ASP.NET Web服务上有一个WebMethod,它返回一个枚举数组。如果添加了一个新值,并且该值由函数调用返回,那么webservice的使用者将抛出一个异常,即使它不关心该枚举值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
[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>