Some time ago I blogged about how to implement a WCF client in C# using VS2008. In particular I mentioned that implementing the client side code with a using statement may potentially lead to problems as – against their own advice – Microsoft chose to implement the Dispose() method of class System.ServiceModel.ClientBase<TChannel> in such a way that it can throw exceptions. (Dispose() implementations should not throw exceptions.) This implementation that violates Microsoft’s own guideline is also described in an article where Microsoft recommends not to use “using” when working with WCF clients.
In this blog post I’ll revisit the options for handling client side code. In the meantime I’ve moved to VS2010 and I’m using .NET 4.0. To get started let’s recall the code from back then:
var client = new PondServiceClient();
try {
_pondStatusMessage.Text = _pondNameTextBox.Text +
(client.IsFrozen(_pondNameTextBox.Text) ?
" is frozen." : " free of ice.");
client.Close();
}
catch (CommunicationException ex) {
// Handle exception
client.Abort();
}
catch (TimeoutException ex) {
// Handle exception
client.Abort();
}
catch (Exception ex) {
// Handle exception
client.Abort();
}
So the canonical form (also described on MSDN) is to check for CommunicationException and TimeoutException. If the service defines Service Fault's you may want to catch those exceptions (FaultException<T>) as well.
It would be great if there were ways to fix this problem as there is one more issue with using the canonical way. Imagine you have lots of touch points between your client and a service. Wouldn’t it be nice if you could avoid all that try-catch business? It looks like a lot of code duplication after all.
Marc Gravell’s Solution
One possible solution is described by Marc Gravell in his blog. He makes extensive use of generics. Using Marc’s solution the above code would look something like this:
using (var client = new Proxy().Wrap()) {
_pondStatusMessage.Text = _pondNameTextBox.Text +
(client.BaseObject.IsFrozen(_pondNameTextBox.Text) ?
" is frozen." : " free of ice.");
}
I agree with Marc that this solution would be safe, easy to call and would eliminate the code duplications. His solution calls the service client’s Dispose() method (ClientBase<TChannel>.Dispose()) but wraps it inside a try-catch construct. If an exception is thrown by the Dispose() it is swallowed.
The first concern I have with this solution is that it doesn’t call Abort() and Close() respectively as per Microsoft’s recommendation. It just swallows the exception that might be thrown by the Dispose method.
The second concern I have is that the expression “new Proxy().Wrap()” doesn’t appear very intuitive and easy to understand. This is probably a minor point as with more experience in using this solution one can will become used to it.
Jeremy Jameson’s Solution
Another option can be found at Jeremy Jameson’s blog. Jeremy suggests to derive a class and add a new implementation for Dispose(). To stay with the pond service example, that new class would then look as follows:
public class PondServiceClientWithDisposeFix
: PondServiceClient, IDisposable {
void IDisposable.Dispose() {
if (this.State == CommunicationState.Faulted) {
this.Abort();
}
else if (this.State != CommunicationState.Closed) {
this.Close();
}
}
}
With this class in place the implementation of the client side could then be re-written as follows:
using(var client = new PondServiceClientWithDisposeFix()) {
_pondStatusMessage.Text = _pondNameTextBox.Text +
(client.IsFrozen(_pondNameTextBox.Text) ?
" is frozen." : " free of ice.");
}
Very elegant, easy to understand and correct by the looks of it. I was tempted to use this option if it wasn’t for a concern that I have with this. With reverse engineering it is possible to find out what ClientBase<TChannel>.Dispose() does. When Jeremy wrote his blog (March 2010) the Dispose() implementation simply called Close().
My concern is that the new implementation doesn’t call the base class implementation at all. By the way the PondServiceClient class is declared partial so the Dispose method could probably be implemented as an extension method as well. As long as the base class implementation is not called, this couldn’t resolve my concern, though. What if Microsoft at any time decides to change the implementation of ClientBase<TChannel>.Dispose()? This kind of change typically happens – thanks, Murphy! – we we expect it the least.
Even if the implementation was never changed, there is a small thing that concerns me. Microsoft has included a little note regarding the use of the ClientBase<TChannel>.State property:
So, Jeremy’s solution probably will work. But it is against Microsoft’s recommendation of not using the State property. In particular I get nervous because of the term “race condition”. If problems occur because of race conditions these are typically hard to find and resolve, therefore a very expensive type of issue.
Therefore if we want to follow that recommendation we are back to square one. We would have to surround the use of ClientBase<TChannel> derived objects with boilerplate try-catch statements.
Using Anonymous Methods
Basically the problem is that in order to remove the code duplication a pair of code snipped has be executed before and after the service invocation. Proper handling of communication objects almost sounds like an aspect… so for a moment I even considered looking into libraries like DynamicProxy2 or Ninject. On second thought, though, these felt like overkill for something that appears very simple.
Enter anonymous methods. Basically you can pass a block of code to a different method that does things, executes the block of code, maybe does other things, then returns. How does this apply to the code snippet for the pond service example above. Let’s try to highlight with colors what the boiler plate code is and what the code is that is different:
The code within green rectangles is the actual implementation while the code in the orange rectangles is the boilerplate code. Note, how the orange pieces surround the second green piece.
The way to simply this is using a class that works like an interceptor. I implemented a class called ServiceClientInterceptor and it’s main method now looks as follows, again using green and orange rectangles as above:
As you can see the green rectangle simply invokes the function that was passed in. With the ServiceClientInterceptor in place you can now write the client side code as follows:
using (var client = new PondServiceClient()) {
new ServiceClientInterceptor(client).Invoke(() => {
_pondStatusMessage.Text = _pondNameTextBox.Text +
(client.IsFrozen(_pondNameTextBox.Text) ?
" is frozen." : " free of ice.");
});
}
Line 2 in this example show the beginning of an anonymous method that is passed in as a parameter to the Invoke() method. The cool thing is that inside of the anonymous method you have access to the variables that are accessible at the point of where the anonymous method is defined. It is not limited to where the intercept invokes the execution of that anonymous method.
As a result you can now wrap service invocations with try-catch statements without code duplication.
With this general concept working there are of course further questions that I haven’t covered in this blog post. For example, what if you want reuse the service client for further invocations in the same using block? What if you had multiple service clients that are used within the same anonymous method? I’ll leave these to you for further explorations as there several options in each case.
The solution presented here meets the following requirements:
- Allows use of using construct
- Properly closes or aborts WCF connections depending on exception thrown
- Avoids code duplication
- Does not make use of ClientBase<TChannel>.State property as per Microsoft’s recommendation
To complete this blog post, below is the full source for the ServiceClientInterceptor class. In that case I have changed the parameter type of the Invoke method to Action. In the screenshots above I still used ServiceMethod which had an identical definition but was the result of earlier experimentation. The Action delegate encapsulates a method that has no parameters and does not return a value. As always you are welcome to use this code at your own risk.
If you know of a solution better than any of the ones mentioned in this post I'd like to hear from you. Happy coding!
public class ServiceClientInterceptor {
public ServiceClientInterceptor(
ICommunicationObject serviceClient) {
_serviceClient = serviceClient;
}
public void Invoke(Action func) {
try {
func.Invoke();
_serviceClient.Close();
}
catch (CommunicationException ex) {
_serviceClient.Abort();
// Handling and/or re-throwing exception is
// application specific
}
catch (TimeoutException ex) {
_serviceClient.Abort();
// Handling and/or re-throwing exception is
// application specific
}
catch (Exception ex) {
_serviceClient.Abort();
// Handling and/or re-throwing exception is
// application specific
}
}
private ICommunicationObject _serviceClient;
}