You might thing you’re special, but your not. And sooner or later it’s gonna happen to you too. Your WCF Service is going to encounter a *gasp* Exception. Do you know what to do when it does happen?
Scott Hanselman wrote a terriffic blog post on exceptions. It’s a great place to start and if you haven’t read it you should. I wrote a post last year about working with the WCF Fault Contract. But there’s more than one way to skin this cat. What I’m going to be focusing on in this series of posts are the ways you can deal with exceptions in WCF and how to communicate what’s happened to your client.
In the first installment we’re going to look at how exceptions get received by a WCF client and how the WCF channel reacts to exceptions. Let’s start with what happens if you just throw a normal .NET exception. For this example we’ll be looking at a mock up of a service to return prices for stocks based on a ticker symbol.
The service contact:
1: [ServiceContract]
2: public interface IMyWcfService
3: {
4: [OperationContract]
5: decimal GetStockPrice(string symbol);
6: }
And the implementation:
1: public class MyWcfService : IMyWcfService
2: {
3: public decimal GetStockPrice(string symbol)
4: {
5: switch (symbol)
6: {
7: case "MSFT":
8: return (decimal) 1.41;
9: case "IBM":
10: return (decimal).89;
11: case "JAVA":
12: return (decimal).10;
13: default:
14: throw new ArgumentOutOfRangeException("symbol", "bad symbol");
15: }
16: }
17: }
Since this is a contrived demo, we only handle three symbols and return a static value. This does however provide us with an opportunity to see how .NET exceptions work in WCF. If we receive a symbol that is not in our list, we are throwing an ArgumentOtOfRangeException. If we were using this as just a .NET object we wouldn’t have a problem; either our code would catch the exception and do something with it, or it would bubble up to the user.
Our client is a winform application. The important part is the call to our service (via a proxy of course):
1: public partial class Form1 : Form
2: {
3: private readonly MyWcfServiceClient _proxy;
4:
5: public Form1()
6: {
7: InitializeComponent();
8: btnCallService.Click += CallService;
9: _proxy = new MyWcfServiceClient();
10: }
11:
12: public void CallService(object sender, EventArgs e)
13: {
14: lblResult.Text = _proxy.GetStockPrice(txtSymbol.Text).ToString();
15: }
16: }
So all we’re doing is grabbing a value (the ticker symbol) from a text box and passing it as an argument to the service. The result is used to set the value of a label control. When we run this and feed one of the three symbols that our service uses, the expected happens: a value is returned and set to the text of the label. When we provide an unsupported symbol, we get an error:
The exception text should look familiar to anyone who has worked with ASP.NET…
System.ServiceModel.FaultException: The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.
Like ASP.NET, WCF will not return detailed exception information to clients as a security measure. Like ASP.NET we have the ability to change the configuration to return more detailed exception information. One line nine below, we have set the includeExceptionDetailInFaults value to “True”:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: ...
5: <behaviors>
6: <serviceBehaviors>
7: <behavior name="Service1Behavior">
8: <serviceMetadata httpGetEnabled="True"/>
9: <serviceDebug includeExceptionDetailInFaults="True" />
10: </behavior>
11: </serviceBehaviors>
12: </behaviors>
13: </system.serviceModel>
14: </configuration>
This results in the details of the error message being passed back to us:
Like ASP.NET, this can be OK for development, but is not a good idea for production.
Let’s make a few changes to our client application:
1: public void CallService(object sender, EventArgs e)
2: {
3: try
4: {
5: lblResult.Text = _proxy.GetStockPrice(txtSymbol.Text).ToString();
6: }
7: catch
8: {
9: MessageBox.Show("Service call failed");
10: }
11: }
By catching the exception we can let the user know that something happened and handle it gracefully. Running the application and use the symbol “ORCL” causes the exception, which is handled:
So, we can just re-submit a supported symbol right…?
Uh-oh.
Turns out that when a WCF service throws a normal .NET exception, it faults the channel. Any subsequent calls to the channel results in a CommunicationObjectFaultedExcption being thrown immediately. The channel cannot be salvaged; your only option is to throw it out and recreate it. The proxy implements an interface called ICommunicatioObject that has a Faulted event that we can subscribe to.A few more changes to our client…:
1: private MyWcfServiceClient _proxy;
2:
3: public Form1()
4: {
5: InitializeComponent();
6: btnCallService.Click += CallService;
7: _proxy = new MyWcfServiceClient();
8: ((ICommunicationObject)_proxy).Faulted += RecreateProxy;
9: }
10:
11: void RecreateProxy(object sender, EventArgs e)
12: {
13: _proxy = new MyWcfServiceClient();
14: }
… and now our client is able to recreate the proxy if a communication fault occurs.
This way of dealing with exceptions in WCF works well if you are a client of a service that you may not control or have the metadata needed to create a fault contract for. Otherwise, using the FaultException class and/or WCF fault contracts are a better way to go. I’ll be covering them more in detail in the next couple posts.
Code on!
Print | posted on Tuesday, August 11, 2009 3:11 PM