WCF Simplified Part 5: Handling Exceptions and Faults
May 25, 2010 3 Comments
Today we’ll look at how WCF handles exceptions. Before we look at the code, a few things to note,
1. Uncaught CLR exceptions are turned into a SOAP fault (FaultException).
2. By default, exception details (even exception type) are not shared with clients.
Use IncludeExceptionDetailsInFaults (includes type, message, and stack trace) for debugging purposes only.
3. Uncaught exceptions will kill WCF sessions.
Let’s see some of these in action,
1. Open the WCF service app from the introductory post (run Visual Studio as admin) and change the IService1.cs to have this,
[DataContract] public class MyCustomException { [DataMember] public string MyMessage { get; set; } } [ServiceContract] public interface IService1 { [OperationContract] string ThrowsCLRException(); [OperationContract(IsOneWay=true)] void ThrowsCLRExceptionOneWay(); [OperationContract] string ThrowsFaultException(); [OperationContract(IsOneWay = true)] void ThrowsFaultExceptionOneWay(); [OperationContract] [FaultContract(typeof(IndexOutOfRangeException))] string ThrowsTypedCLRFaultException(); [OperationContract] [FaultContract(typeof(MyCustomException))] string ThrowsTypedCustomFaultException(); }
2. Change the implementation file, Service1.cs to this,
public class Service1 : IService1 { public string ThrowsCLRException(){ // Throw a CLR exception throw new IndexOutOfRangeException("ThrowsCLRException: IndexOutOfRangeException"); } public void ThrowsCLRExceptionOneWay() { throw new IndexOutOfRangeException("ThrowsCLRExceptionOneWay: IndexOutOfRangeException"); } public string ThrowsFaultException() { // Throw a fault exception throw new FaultException("ThrowsFaultException: FaultException", new FaultCode("CalculationError")); } public void ThrowsFaultExceptionOneWay() { throw new FaultException("ThrowsFaultExceptionOneWay: FaultException"); } public string ThrowsTypedCLRFaultException() { // Throw a typed FaultException with a CLR exception throw new FaultException<IndexOutOfRangeException>(new IndexOutOfRangeException(), new FaultReason("ThrowsTypedCLRFaultException: FaultException<IndexOutOfRangeException>")); } public string ThrowsTypedCustomFaultException() { // Throw a typed FaultException with a custom exception MyCustomException fault = new MyCustomException(); fault.MyMessage = "ThrowsTypedCustomFaultException: FaultException<MyCustomException>"; throw new FaultException<MyCustomException>(fault, new FaultReason("No reason!")); } }
using (ServiceHost host = new ServiceHost(typeof(WcfServiceLibrary1.Service1), new Uri("http://localhost:8000/HelloWCF"))) { // Set up a service endpoint [Contract, Binding, Address] host.AddServiceEndpoint(typeof(WcfServiceLibrary1.IService1), new BasicHttpBinding(), "HelloWCF"); // Enable metadata exchange ServiceMetadataBehavior smb = new ServiceMetadataBehavior() { HttpGetEnabled = true }; host.Description.Behaviors.Add(smb); // Enable exeption details ServiceDebugBehavior sdb = host.Description.Behaviors.Find<ServiceDebugBehavior>(); sdb.IncludeExceptionDetailInFaults = false; host.Open(); Console.WriteLine("Ready..."); Console.ReadLine(); }
4. Now run the service app and update the Service Reference in the client console app. Change the client code to,
EndpointAddress epoint = new EndpointAddress("http://localhost:8000/HelloWCF/HelloWCF"); ServiceReference1.IService1 proxy = ChannelFactory<ServiceReference1.IService1>.CreateChannel(new BasicHttpBinding(), epoint); using (proxy as IDisposable) { // Even though a CLR exception is thrown; WCF converts it to a FaultException on the client try { proxy.ThrowsCLRException(); } catch (FaultException fex) { Console.WriteLine("1. " + fex.Message); } Console.WriteLine(); // One-way - no reply message - nothing gets caught try { proxy.ThrowsCLRExceptionOneWay(); } catch (FaultException fex) { Console.WriteLine("2. " + fex.Message); } catch (Exception ex) { Console.WriteLine("2. " + ex.Message); } Console.WriteLine(); // WCF service threw a FaultException try { proxy.ThrowsFaultException(); } catch (FaultException fex) { if (fex.Code.Name == "CalculationError") { Console.WriteLine("3. " + fex.Message); } } Console.WriteLine(); // One-way - no reply message - nothing gets caught try { proxy.ThrowsFaultExceptionOneWay(); } catch (FaultException fex) { Console.WriteLine("4. " + fex.Message); } catch (Exception ex) { Console.WriteLine("4. " + ex.Message); } Console.WriteLine(); // WCF service threw a typed FaultException - note the catch try { proxy.ThrowsTypedCLRFaultException(); } catch (FaultException<IndexOutOfRangeException> fex) { Console.WriteLine("5. " + fex.Message); } Console.WriteLine(); // WCF service threw a typed FaultException - note the catch try { proxy.ThrowsTypedCustomFaultException(); } catch (FaultException<ServiceReference1.MyCustomException> fex) { Console.WriteLine("6. Reason:" + fex.Message); Console.WriteLine(fex.Detail.MyMessage); } } Console.ReadLine();
5. When you run this code, you’ll see,
Some things to note,
1. There is no output for ThrowsCLRExceptionOneWay and ThrowsFaultExceptionOneWay obviously.
2. In the ThrowsCLRException case – a CLR exception is caught as a FaultException, but there are no associated details about the exception.
3. We can throw a typed FaultException using a <CLRException> or a <CustomException>.
Now to see CLR exception details, in step3 change to,
sdb.IncludeExceptionDetailInFaults = true;
Now running the client you should get the CLR exception details,
The last thing to discuss is that throwing a CLR exception will actually kill the WCF session/channel, but a FaultException will not. Since this example was written using BasicHttpBinding which has no concept of WCF sessions, everything worked. To see the problem change the binding on both the host app and the client app to use WSHttpBinding. Now running the service and client results in,
because the WCF session was crashed by the thown CLR exception (even though it shows up as a FaultException on the client). This can be fixed by designing your service code to catch all CLR exceptions and always throwing FaultException from the service.
Pingback: WCF Simplified Series « I.Net
Hi, when I run this example, the exception is not propagated to the client.
I’m receiving the message “IndexOutOfRangeException was unhandled by user code” in the method ThrowsCLRException at the server application.
Do you know which could be the problem?
Thanks
Good one