I have a WebMethod on an ASP.NET webservice which is returning an array of an Enum. If a new value is added, and that value is returned by a function call, then a consumer of the webservice will throw an exception, even though it doesn't care about that enum value.
[WebMethod]
public UserRole[] GetRoles(string token)
partial 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>
(Consumer is compiled with this wsdl, but then wsdl changes and a new value is now allowed - if that value is returned, an XML exception is thrown by the client.)
Is there any way to override SOAP deserialization for this type so that I can catch the error and either remove that item from the array or replace it with a default value? If this was using JSON instead of XML, I could register a JsonConverter to handle that type, so I guess I'm looking for a similar global "RegisterConverter" type function. Which I don't think exists, but hoping for some help...
Any means of decorating the Enum with Attributes will not work, because all the code is generated by the wsdl and is regenerated when the web reference is updated. Normally if I want to modify a class that was generated by a wsdl, I can create a partial class, but that doesn't work for an Enum. And not even sure if I could override the XmlSerialization code even if it was a class.
Some additional background:
This is actually implemented as my attempt at a dynamic Enum. The wsdl is generated from a database lookup so that I can add extra values to the database and the consuming application will have access to the allowed values without having to recompile the webservice. This way I get intellisense and constraint enforcement via the enum type, but the ability to add values without tightly coupling the webservice code and the client code. The problem is that if I add a new value, it creates the potential to break consumers that aren't updated with the new wsdl... I would much rather just ignore that value, since the consumers wouldn't know what to do with it anyway.
A SOAP Extension might be the way to fix this (I know how to add a SOAP Extension to the WebService itself, but have no idea how to add one on the client side...), but it's not ideal because I'd really like to have a generic way of handling this easily so I can have more dynamic enums in my code (they're not really dynamic, but the idea is that the values pass through the middle layer of the webservice without having to recompile that middle layer). Something like "XmlSerialization.RegisterConverter(MyDynamicEnumType, DynamicEnum.Convert)" would be ideal, where I can define a generic function to use and register.)
Still hoping that someone else will have an answer, but I at least came up with something a little better than my original thought of using Regex.Replace to strip out references to enum values I didn't recognize.
I am using a partial class for the web service and overriding GetReaderForMessage as below:
This is the definition for EnumSafeXmlReader:
I also added a new value for UserRole -
UserRole.Unknown, and made sure that it is the first in the list of allowed values.So as long as a value of this enum is wrapped in a tag with the type name
<UserRole>UnexpectedRole</UserRole>, if it is not recognized, it will be replaced withUserRole.Unknown, which my client can happily ignore. Note this could also break if there was another tag calledUserRolethat was not of this enum type and was expected be, say, string or int. It's fairly brittle.This solution still leaves a lot to be desired, but it will generally work on lists of enum values...
This will end up resulting in a
UserRole[]that containsUserRole.InvoiceEntryandUserRole.Unknown.But if I have a field or property of type UserRole:
this will still fail, because the Reader has no way of knowing that "PrimaryRole" needs to deserialize to type UserRole. The XmlSerializer knows this, but as far as I can tell, there is no way to override the XmlSerializer, only the XmlReader.
I suppose it's not entirely impossible to give the EnumSafeXml Reader enough information to recognize tags that will deserialize to an enum type, but it's more trouble than I'm willing to go to right now - I specifically need it to work in the "array of enum values" case, which it now does.
I did added some caching on Types so that I only have to check a Tag name once to see if it's also the name of an enum, but I removed that for clarity in this example.
I welcome any other possible solutions or recommendations for improving this solution.