Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fail early or clearly throw when an enum cannot be serialized

Tags:

c#

wcf

.net-4.0

In the scenario where a WCF service returns a DataContract with an enum member that has an invalid value (an int not present in the Enum type) the thrown exception on the client side is The underlying connection was closed: The connection was closed unexpectedly.
Strangely enough this exception is triggered because the DataContractSerializer cannot serialize at the serverside of the connection.

I would rather have something more usefull thrown at me and more important earlier, at runtime on the serverside but maybe a compiler warning...

WCF Service Contract

    [ServiceContract]
    public interface IDtoService
    {
        [OperationContract]
        MyDto GetData(int value);
    }

    public enum Rating
    {
        None = 0,
        NotSet = 1,
        Somevalue = 34
    }

    [DataContract]
    public class MyDto
    {
        Rating _rate;

        [DataMember]
        public Rating Rating 
        { 
            get { return _rate; } 
            set 
            {
                _rate = value; 
            } 
        }

    }

Service implementation

    public class DtoService : IDtoService
    {
        public MyDto GetData(int value)
        {
            var dto = new MyDto {  Rating = (Rating) 42 }; // not in ENUM!
            return dto;
        }
    }

Client

var cl = new DtoServiceClient.DtoServiceClient();
var tada = cl.GetData(1);  // connection closed exception

To have the actual exception thrown I had to disbale 'enable just my code' in the debug options VS2010 and in the Exceptions dialog enable Thrown for 'Common language Runtime exceptions'

With that settings the valuable exception is:

SerializationException
Enum value '42' is invalid for type 'WcfService1.Rating' and cannot be serialized. Ensure that the necessary enum values are present and are marked with EnumMemberAttribute attribute if the type has DataContractAttribute attribute

This comes at the cost that the debugger gets very noisy about all other kinds of thrown exceptions that AFAIK can be ignored.

What can I try to have this exception thrown without the noise?

Is there something I can tweak or add in the WCF pipeline?

One possible solution to fail early with the cost of rework is adding an extra Assert in the setter of the enum member:

Debug.Assert(Enum.IsDefined(typeof(Rating), value),"value is not valid for this enum");

An alternative I tried was adding a Contract in the hope a warning could be produced. I couldn't find any thing better than basically a copy of the Debug.Assert.

System.Diagnostics.Contracts.Contract.Assert(Enum.IsDefined(typeof(Rating), value));

Is there an option to have the compiler emit this check for me or is there an alternative I'm not aware of?
(I also tried to compile with the check for arithmetic overflow/underflow enabled without expecting it to be succeful)

CodeContracts does emit a warning, (you have to enble Implicit Enum write obligations) though that does not really help

The actual value may not be in the range defined for this enum value

This behavior is in a VS2010/.Net 4.0 context.

like image 584
rene Avatar asked Jul 24 '13 12:07

rene


1 Answers

You could do a few things.

On the WCF side, use IncludeExceptionDetailInFaults (either using a ServiceBehavior attribute or in your app.config). This will make WCF send detailed exceptions to the client. Note this is considered an 'unsafe' setting since it exposes server stack traces to the client side, so you should only use that during development. For production, you should use a WCF error handler to log all service errors (or turn on WCF tracing).

If you want to use Code Contracts to catch this error during compile time you can use object invariants:

public class MyDto
{
    public Rating Rating { get; set; }

    [ContractInvariantMethod]
    void Invariant()
    {
        Contract.Invariant(Enum.IsDefined(typeof(Rating), Rating));
    }
}

If you enable static analysis, you will receive a warning on this line:

new MyDto {  Rating = (Rating) 42 };
like image 158
Eli Arbel Avatar answered Sep 19 '22 20:09

Eli Arbel