Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-defined enumeration values in WCF service

Tags:

c#

wcf

We have a WCF service that exposes a "Customer" type, i.e.:

[DataContract(Name = "CustomerData", Namespace = "http://www.testing.com")]
public partial class Customer
{   
    [DataMember]
    public CustomerLevel Level
    {
        get;
        set;
    }   
}

You can see the above type has a property that is an enumeration type. The definition for this enum is:

[IgnoreCoverage]
[DataContract(Namespace = "http://www.testing.com"")]
public enum CustomerLevel : int
{

    [EnumMember(Value = "Platinum")]
    Platinum = 1,

    [EnumMember(Value = "Gold")]
    Gold = 2,

    [EnumMember(Value = "Silver")]
    Silver = 3,

    [EnumMember(Value = "Bronze")]
    Bronze = 4,
}

The service works fine as long as the server sends a valid enumeration for each customer that it returns. However, if the service returns a CustomerLevel that is not defined in the enumeration the service call times out.

An example of a bad CustomerLevel value might be:

customer.Level = (CustomerLevel)0;

The service also times out if the client attempts to send a non-defined value.

Is there any way to allow the non-defined value to flow through to both the client and server and let each of them handle the bad value on their own?

like image 437
user89166 Avatar asked Feb 28 '23 20:02

user89166


2 Answers

I don't think you're going to get bogus enums to work. What would they deserialize into? If you mean for the client to send integers, then change the type to int, and convert it into the enum on your own (with your own error handling).

As to whether the client should time out, please tell us what sort of client you're using. Also, I recommend you look at the network traffic and see how the service responded, if at all. Also look in the Windows event logs to see if the service complained at all. Finally, you may want to turn on WCF tracing to see how the service is reacting to this.

like image 54
John Saunders Avatar answered Mar 12 '23 18:03

John Saunders


You can do this using a custom IDataContractSurrogate to map the enum to an int then back to an enum.

The documentation for creating such surrogates is here: http://msdn.microsoft.com/en-us/library/ms733064.aspx

Here is a generic version I developed that can handle a list of Enum types. You specify this in the constructor for your DataContractSerializer. For more information, see my blog post here: http://www.shulerent.com/2012/08/13/handling-invalid-enum-values-in-a-datacontractserializer/

/// <summary>
/// IDataContractSurrogate to map Enum to int for handling invalid values
/// </summary>
public class InvalidEnumContractSurrogate : IDataContractSurrogate
{
    private HashSet<Type> typelist;

    /// <summary>
    /// Create new Data Contract Surrogate to handle the specified Enum type
    /// </summary>
    /// <param name="type">Enum Type</param>
    public InvalidEnumContractSurrogate(Type type)
    {
        typelist = new HashSet<Type>();
        if (!type.IsEnum) throw new ArgumentException(type.Name + " is not an enum","type");
        typelist.Add(type);
    }

    /// <summary>
    /// Create new Data Contract Surrogate to handle the specified Enum types
    /// </summary>
    /// <param name="types">IEnumerable of Enum Types</param>
    public InvalidEnumContractSurrogate(IEnumerable<Type> types)
    {
        typelist = new HashSet<Type>();
        foreach (var type in types)
        {
            if (!type.IsEnum) throw new ArgumentException(type.Name + " is not an enum", "type");
            typelist.Add(type);
        }
    }

    #region Interface Implementation

    public Type GetDataContractType(Type type)
    {
        //If the provided type is in the list, tell the serializer it is an int
        if (typelist.Contains(type)) return typeof(int);
        return type;
    }

    public object GetObjectToSerialize(object obj, Type targetType)
    {
        //If the type of the object being serialized is in the list, case it to an int
        if (typelist.Contains(obj.GetType())) return (int)obj;
        return obj;
    }

    public object GetDeserializedObject(object obj, Type targetType)
    {
        //If the target type is in the list, convert the value (we are assuming it to be int) to the enum
        if (typelist.Contains(targetType)) return Enum.ToObject(targetType, obj);
        return obj;
    }

    public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
    {
        //not used
        return;
    }

    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        //Not used
        return null;
    }

    public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
    {
        //not used
        return null;
    }

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        //not used
        return null;
    }

    public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
    {
        //not used
        return typeDeclaration;
    }

    #endregion
}
like image 32
Jason Shuler Avatar answered Mar 12 '23 19:03

Jason Shuler