none
Enum accepting any string RRS feed

  • Question

  • I have an asynchronous WCF service and was getting the following exception:

    An error occurred while receiving the HTTP response to http://localhost:34840/La5108Service.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.

    Upon investigation, I discovered that my enum was being assigned a wrong value.  However, the exception didn't happen when parsing the string to an enum, it happened when the object was deserialized by our MVC 4 application.

    This struck me as very curious, so I went to the line in my repository where the crash SHOULD have occurred.

    ActionType = dataReader.GetEnum<ActionType?>("ActionTypeDescription")

    GetEnum is an extension method that looks like this:

            public static T GetEnum<T>(this DbDataReader dbDataReader, String columnName)
            {
                Object currentValue = ReadColumn(dbDataReader, columnName);;
                if (IsNull(currentValue))
                    return default(T);
                Type enumType = typeof(T);
                // get generic arguments
                Type[] arguments = enumType.GetGenericArguments();
                // if it is a nullable type, reassign enumType to the enum type
                if (arguments != null && arguments.Length > 0)
                    enumType = arguments[0];
                return (T)Enum.Parse(enumType, currentValue.ToString());
            }

    Then I created a console application and ran this exact code and it crashed, as it should have (when trying to assign a value to the enum that doesn't exist).  But yet, works in WCF (crashes when it gets deserialized by the client).  This has made me come to the conclusion that WCF doesn't actually work with real types, instead, it works with serialized version of that type under the hood.  Can anyone confirm or deny this and provide proof of what/why WCF is doing?

    Friday, March 22, 2013 5:21 PM

Answers

  • Hi da.3vil.coder

    This is a known .net gotcha. You can set any integer to an enum. There is no validation. Try this:

        class Program
        {
            static void Main(string[] args)
            {
                MyEnum _x = (MyEnum)9;
                Console.WriteLine(_x);
                Console.ReadLine();
            }
        }
    
        enum MyEnum
        {
            Value1 = 1,
            Value2 = 2
        }

    The serializer however needs to know what member to match the value to. It cannot and hence the exception. The server cannot send a reply to the client.

    If you receive integer values from services or UI you should validate them before storing them in your database.

    You can use the Enum.IsDefined to help validate your input.

    Console.WriteLine(Enum.IsDefined(typeof(MyEnum), 1));
    Console.WriteLine(Enum.IsDefined(typeof(MyEnum), 3));
     

    Br,

    JAXN

     

     


    Please mark posts as answers/helpful if it answers your question/or helped you solve your problem.
    MCTS: .NET Framework 4, Service Communication Applications


    • Marked as answer by da.3vil.coder Monday, March 25, 2013 6:02 PM
    • Edited by JAXN Monday, March 25, 2013 8:20 PM
    Monday, March 25, 2013 5:31 PM

All replies

  • Hi da.3vil.coder

    WCF will send the a string representation of the enum value on the wire.

    Could you post the following:

    The enum code on the server, the enum code on the client (if you are not using a shared assembly) and the value traveling on the wire. You can trace the message using message tracing - on both the client and the server.

    Configuring Message Logging
    http://msdn.microsoft.com/en-us/library/ms730064.aspx

    Br,

    JAXN


    Please mark posts as answers/helpful if it answers your question/or helped you solve your problem.
    MCTS: .NET Framework 4, Service Communication Applications

    Monday, March 25, 2013 10:54 AM
  • Here is the enum:

        [DataContract]
        public enum ActionType
        {
            [EnumMember]
            Attorney = 1,
    
            [EnumMember]
            SuitAction = 2,
    
            [EnumMember]
            SuitDate = 3,
    
            [EnumMember]
            La5108 = 4
        }

    Here is the entity that contains the enum and is returned by the service:

        [DataContract]
        public class SuitAction : IsActiveBase
        {
            Entities.Action action;
            DateTime? actionDate, dateStamp, modifiedDate;
            String comment;
            User createdByUser, modifiedByUser;
            Int32? suitId;
    
            [DataMember]
            public Entities.Action Action
            {
                get { return action; }
                set { action = value; }
            }
    
            [DataMember]
            public DateTime? ActionDate
            {
                get { return actionDate; }
                set { actionDate = value; }
            }
    
            [DataMember]
            public String Comment
            {
                get { return comment; }
                set { comment = value; }
            }
    
            [DataMember]
            public User CreatedByUser
            {
                get { return createdByUser; }
                set { createdByUser = value; }
            }
    
            [DataMember]
            public DateTime? DateStamp
            {
                get { return dateStamp; }
                set { dateStamp = value; }
            }
    
            [DataMember]
            public User ModifiedByUser
            {
                get { return modifiedByUser; }
                set { modifiedByUser = value; }
            }
    
            [DataMember]
            public DateTime? ModifiedDate
            {
                get { return modifiedDate; }
                set { modifiedDate = value; }
            }
    
            [DataMember]
            public Int32? SuitId
            {
                get { return suitId; }
                set { suitId = value; }
            }
        }
    The code that loads it from the database:
            IList<SuitAction> la5108ActionList = new List<SuitAction>();
                if (dataReader.NextResult())
                    while (dataReader.Read())
                        la5108ActionList.Add(new SuitAction()
                        {
                            ActionType = dataReader.GetEnum<ActionType?>("ActionTypeDescription") 
                            // other fields omitted for brevity 
                        });
                return la5108ActionList;

    The value that comes back from the database is 5108.  This is also the value that gets assigned to the enum, which C# accepts for some weird reason.  The assembly is shared, so the client receives a list of SuitAction entities.


    Monday, March 25, 2013 1:08 PM
  • Want to make a small correction here.  It doesn't crash on deserialization, it crashes on serialization. 

    The InnerException message was 'Enum value '5108' is invalid for type 'Litigation.Entities.ActionType' and cannot be serialized.

    The above error I got from the tracelog and not from WCF.

    However, this doesn't change the fact that C# is allowing the invalid value of 5108 to be assigned to the enum in WCF.  It doesn't do this in a console or windows application. 

    Here's a screenshot from the code after doing the database get.  Notice it has the invalid value assigned to the enum.


    Monday, March 25, 2013 2:38 PM
  • Hi da.3vil.coder

    This is a known .net gotcha. You can set any integer to an enum. There is no validation. Try this:

        class Program
        {
            static void Main(string[] args)
            {
                MyEnum _x = (MyEnum)9;
                Console.WriteLine(_x);
                Console.ReadLine();
            }
        }
    
        enum MyEnum
        {
            Value1 = 1,
            Value2 = 2
        }

    The serializer however needs to know what member to match the value to. It cannot and hence the exception. The server cannot send a reply to the client.

    If you receive integer values from services or UI you should validate them before storing them in your database.

    You can use the Enum.IsDefined to help validate your input.

    Console.WriteLine(Enum.IsDefined(typeof(MyEnum), 1));
    Console.WriteLine(Enum.IsDefined(typeof(MyEnum), 3));
     

    Br,

    JAXN

     

     


    Please mark posts as answers/helpful if it answers your question/or helped you solve your problem.
    MCTS: .NET Framework 4, Service Communication Applications


    • Marked as answer by da.3vil.coder Monday, March 25, 2013 6:02 PM
    • Edited by JAXN Monday, March 25, 2013 8:20 PM
    Monday, March 25, 2013 5:31 PM
  • Ah...that clears up everything.  I didn't know about that caveat in .NET.  Thank you for the insight.
    Monday, March 25, 2013 6:02 PM