0

I know that casting an integer to an invalid enum value does not result in an exception in C# due to Flags. Here I was, specifying a table in a DB table and getting its values 1-3 with EntityFramework. On my serverside I cast the values into an enum XyzType ranging from 0-2, which obviously generates the invalid value (XyzType)3. Now i put this value in a XyzDTO and send it to a WCF client. Which upon deserialization fails with this very vague description:

Exception from deserialization

Now i've read some other posts discussing exceptions caused by deserializing invalid XML and it all makes sense.

My question is, how come I did not get any useful information as to what the cause was at all? I spent the last 1.5 hours with this bug, until i figured the difference between the enum and the DB values. Is it considered a flaw in the WCF error handling that no useful info was given? If so, should MS be notified? Do you have any good practices as to how to avoid scenarios like this in the future?

EDIT: Please note i do not have a web.config. My serverside is a WCF service within a windows service, ie. i have app.config.

EDIT2: There seems to me some misunderstanding regarding this issue and what ive tried. Inspecting the InnerExceptions of the CommunicationException in the debugger provides no useful information at all. Also i already have "includeExceptionDetailsInFaults" = true on serverside, but the exception is not thrown by the serverside! It is caused during deserialization on clientside.

EDIT3: Some answers have suggested activating tracing on clientside. I have now attempted this, but apparantly for this issue it does not provide any useful information either. Here is a link to the output.

LuqJensen
  • 310
  • 6
  • 14

4 Answers4

0

It's almost always a good idea to activate tracing to get some more detailed informations about the error:

<configuration>
   <system.diagnostics>
      <sources>
            <source name="System.ServiceModel" 
                    switchValue="Information, ActivityTracing"
                    propagateActivity="true">
            <listeners>
               <add name="traceListener" 
                   type="System.Diagnostics.XmlWriterTraceListener" 
                   initializeData= "c:\log\Traces.svclog" />
            </listeners>
         </source>
      </sources>
   </system.diagnostics>
</configuration>

You can configure tracing in your server's and client's web- or app.config files to get details from both sides.

Edit:

<serviceDebug includeExceptionDetailInFaults="true" /> makes your service transport an occured exception (see here for more details) to the client. If the client doesn't handle that fault, you might won't notice it.

Jan Köhler
  • 5,817
  • 5
  • 26
  • 35
  • Hi, i do not have a web.config. I have an app.config on both client and server side. I already have specified to include exception details on serverside. Does your solution provide any more details? Is it the same syntax for both client and server? ` ` – LuqJensen Mar 29 '16 at 13:09
  • I wasnt the one to downvote, but could you clarify your answer please? I already have include exception details set to true on serverside. But does the code you provided grant me any more information from either client or serverside? is it applicable on clientside where deserialization fails? – LuqJensen Mar 29 '16 at 13:44
  • Well, I didn't think so ;) And yes, you can apply this to the server _and_ to the client side. It might provide very helpful information, as it might give some insights into the deserialization which seems to be your problem point. – Jan Köhler Mar 29 '16 at 14:04
  • Hi sorry to say but it still doesnt help the problem. I tried your code and all it does is give me a log with the exact same useless information. See EDIT3 in the question. It does look like it could be useful for other scenarios however. – LuqJensen Mar 29 '16 at 14:34
  • Sad to hear. What you could still try is to inspect the traffic to look into the messages right before the client receives a message and starts deserializing. Give [Fiddler](http://www.telerik.com/fiddler) a try. – Jan Köhler Mar 29 '16 at 16:55
0

When discussing enums and WCF, ensure the values are serialized/deserialized by marking up the enum with the DataContract and EnumMember attributes as follows:

[DataContract(Namespace = Namespaces.V1)]
public enum AreaType : int
{
    [EnumMember]
    Unknown = 0,
    [EnumMember]
    Urban = 1,
    [EnumMember]
    Suburban = 2,
    [EnumMember]
    Rural = 3
}

It seems obvious, but also be aware that the default value is 0. As khlr mentioned, you can activate tracing. You will want to set the customErrors mode to "Off" and compilation for debug to "true" in your web.config while in Development.

<system.web>
    <compilation debug="true" />
    <customErrors mode="Off" />
</system.web>

Once you've moved off development, you'll want to provide a custom error page and set debug="false".

<system.web>
    <compilation debug="false" />
    <customErrors mode="RemoteOnly" defaultRedirect="ErrorRedirectPage.html" />
</system.web>

You'll also want to give some thought and design consideration to exception handling. You'll need to ensure you do not lose info. on the exception if you're rethrowing the exceptions. I like to use a simple logging framework, such as NLog, to log the exception, then I'll rethrow the exception back to the original method in the service where I wrap the exception in a FaultException where T is a class that I create for serializing and holding exception information. For example:

[DataContract(Namespace=Namespaces.V1)]
public class MyFaultException
{
    [DataMember]
    public string Reason { get; set; }

    public MyFaultException()
    {
        this.Reason = "Service encountered an error";
    }

    public MyFaultException(string reason)
    {
        this.Reason = reason;
    }
}

And in my service method:

try
{
    //...
}
catch (Exception ex)
{
        // Asynchronously log the error message in a helper class using NLog
        LogHelper.LogException(ex, "Error calculating blah.", LogLevel.Error);

        throw new FaultException<MyFaultException>(
                new MyFaultException(ex.Message), new FaultReason(ex.Message));
}

You might want to define your own classes that derive from Exception to throw your own exceptions.

iCode
  • 1,254
  • 1
  • 13
  • 16
  • Hi, i do not have a web.config. I should update my post... It is nice of you to go into this much detail about standard practices, but you see it isnt applicable as invalid enum casts do not throw exceptions. Altering the error output on clientside could be the solution however. – LuqJensen Mar 29 '16 at 13:26
  • No problem. Just providing some examples of common practices. I do understand that the CommunicationException will not fall into the exception handling practices that I provided above. Yes, you can catch this on the client side. I'll typically catch FaultException, TimeoutException, and CommunicationException explicitly on the client side. In both a web.config or as in your case in an app.config, you'll want to include a servicebehavior for serviceDebug includeExceptionDetailInFaults="True" as mentioned by others. http://stackoverflow.com/questions/6906953/wcf-communication-exception – iCode Mar 29 '16 at 13:34
  • Those others is myself :) Is that part applicable to clientside though? I use it serverside – LuqJensen Mar 29 '16 at 13:39
  • Lol. Yes I see that. I ran out of characters and tried to be succinct and ended up not making sense. Anyway, the includeExceptionDetailsInFaults is applied to the server side config file. MSDN says, "Setting IncludeExceptionDetailInFaults to true enables clients to obtain information about internal service method exceptions". Check the InnerException when you catch it client side. You might still need to use tracing to see the details for CommunicationException. – iCode Mar 29 '16 at 13:49
  • Now i hate to break it to you, but i also specified in my post that casting to an invalid enum value will not throw an exception serverside. Thus setting this attribute serverside will not provide me anything, i just have it for developing purposes. But in this case its useless. The exception happens when the client is deserializing the XML content, it is not present until that moment. I have also inspected the CommunicationException in the debugger. none of the many nested InnerExceptions provide any useful info. – LuqJensen Mar 29 '16 at 13:55
  • Ok. I'll leave it at this. When I've had the same issue, I will immediately suspect serialization/deserialization. Just as you experienced, it took me a while the first time. After some SO and web browsing, it seems with the exception occurring during deserialization that you will not get the details without enabling tracing. This blog post offers some details of enabling tracing with an example: https://coab.wordpress.com/2010/03/08/quickly-finding-wcf-serializationdeserialization-issues/ – iCode Mar 29 '16 at 14:06
  • I tried the blogpost. It is the same as the answer provided by khlr and does not provide any more info. All it does is log the useless info. Please see EDIT3 in the updated question. – LuqJensen Mar 29 '16 at 14:36
0

You're having problems with serialization/deserialization and as others already suggested you need more logging. And you've enabled some of it, but not all and not specific for serialization.

Try to use following config for logging:

<configuration>
   <system.diagnostics>
      <sources>
            <source name="System.ServiceModel" 
                    switchValue="Information, ActivityTracing"
                    propagateActivity="true">
               <listeners>
                   <add name="traceListener" />
               </listeners>
            </source>
            <source name="System.ServiceModel.MessageLogging">
               <listeners>
                   <add name="traceListener" />
               </listeners>
            </source>
            <source name="System.Runtime.Serialization" 
                    switchValue="Information">
               <listeners>
                   <add name="traceListener" />
               </listeners>
            </source>
      </sources>
      <sharedListeners>
          <add name="traceListener" 
               type="System.Diagnostics.XmlWriterTraceListener" 
               initializeData= "c:\log\Traces.svclog" />
      </sharedListeners>
    </system.diagnostics>
    <system.serviceModel>
        <diagnostics>
            <messageLogging 
                logEntireMessage="true" 
                logMessagesAtServiceLevel="true"
                maxSizeOfMessageToLog="16777216"/>
        </diagnostics>    
    </system.serviceModel>
</configuration>

Here I added two additional trace sources (and enabled message logging): System.Runtime.Serialization will produce any errors (if any) regarding serialization/deserialization process. And message logging will help you understand whether you receive anything on client side.

From callstack in the log you've published it's clear to me that exception happens on the server side. But message logging will show this better.

Igor Labutin
  • 1,406
  • 10
  • 10
0

If you want to use Enum in WCF, you need to define 0 value for Enum. It is very important.

public enum MyEnum{
   Default = 0,
   SomeValue1 = 1,
   SomeValue2= 2,
   ....
}