ChannelAdam WCF Library — Version 1 Documentation
Retry for Transient Fault Handling
Overview
When you call your web services, do you need to handle transient errors that may occur (such as the network briefly dropping out), and have some automatic retry logic?
The ChannelAdam WCF Library has some built-in support for the Microsoft Practices Transient Fault Handling Library - but in Version 1, only when you use the Consume
method and not when you use the Operations
property. (Note: this has changed in Version 2).
Different Ways To Retry
Below is a description about different ways that you could implement retry functionality:
- Naively - all custom code
- With the Microsoft Practices Transient Fault Handling Library; or
- More cleanly with the Consume Method
1. Naive Retry Logic with the Operations Property
Below is an example naive implementation of retry logic (without the help of any library) while using the Operations
property.
1int retryCount = 1;
2Exception lastException = null;
3
4using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
5{
6 while (retryCount > 0)
7 {
8 Console.WriteLine("#### Retry countdown: " + retryCount);
9
10 try
11 {
12 int actual = service.Operations.AddIntegers(1, 1);
13 break;
14 }
15 catch (FaultException fe)
16 {
17 lastException = fe;
18 Console.WriteLine("Service operation threw a fault: " + fe.ToString());
19 }
20 catch (Exception ex)
21 {
22 lastException = ex;
23 Console.WriteLine("Technical error occurred while calling the service operation: " + ex.ToString());
24 }
25
26 retryCount--;
27 }
28}
29
30if (lastException != null)
31{
32 ...
33}
Unfortunately that code is lengthy and error prone.
2. Retry Logic with Operations Property and the Microsoft Library
The code above can be improved upon by using the Microsoft Practices Transient Fault Handling Library, as below.
You will notice in this example, I am using the ChannelAdam.ServiceModel.SoapFaultWebServiceTransientErrorDetectionStrategy
to determine if there was a transient error or not.
If the given exception was a FaultException
, this strategy says that was not a transient error and therefore do not retry.
If on the other hand any other type of exception occurs, then this strategy will tell the Microsoft library to retry according to the retry policy.
1using Microsoft.Practices.TransientFaultHandling;
2...
3
4using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
5{
6 int actual = 0;
7 var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2));
8 var retryPolicy = new RetryPolicy<SoapFaultWebServiceTransientErrorDetectionStrategy>(retryStrategy);
9
10 try
11 {
12 retryPolicy.ExecuteAction(() =>
13 {
14 actual = service.Operations.AddIntegers(1, 1);
15 });
16 }
17 catch (FaultException fe)
18 {
19 Console.WriteLine("Service operation threw a fault: " + fe.ToString());
20 }
21 catch (Exception ex)
22 {
23 Console.WriteLine("Technical error occurred while calling the service operation: " + ex.ToString());
24 }
25}
3. Retry Logic with the Consume Method
For me, both code examples above that use the Operations
property is clunky - which is why I prefer using the Consume
method!
When using the Consume
method, there are three ways to set the retry policy:
- Setting the static property
DefaultRetryPolicy
on theServiceConsumerFactory
- which will apply to all created instances; - As an overload to the
Create
method on theServiceConsumerFactory
- which will apply just to thatServiceConsumer
instance to be created; or - As one of the overloads on the
Consume
method itself.
For example, the overload on the Consume
method:
1using Microsoft.Practices.TransientFaultHandling;
2...
3
4var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2));
5var retryPolicy = new RetryPolicy<SoapFaultWebServiceTransientErrorDetectionStrategy>(retryStrategy);
6
7using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
8{
9 var result = service.Consume(operation => operation.AddIntegers(1, 1), retryPolicy);
10
11 if (result.HasNoException)
12 {
13 Console.WriteLine("Actual: " + result.Value);
14 ...
15 }
16 else
17 {
18 if (result.HasFaultException)
19 {
20 Console.WriteLine("Service operation threw a fault: " + result.Exception.ToString());
21 }
22 else if (result.HasException)
23 {
24 Console.WriteLine("An unexpected error occurred while calling the service operation: " + result.Exception.ToString());
25 }
26 }
27}
Perhaps you too may agree that using the retry strategy with the Consume
method is simpler and cleaner, in comparison with the Operations
property.
Note: The retry functionality has changed significantly in Version 2.
comments powered by Disqus