Usage With Inversion of Control (IoC) Containers

ChannelAdam WCF Library — Version 2 Documentation

Usage With Inversion of Control (IoC) Containers

It is easy to use the ServiceConsumerFactory from Inversion of Control (IoC) containers.

Below are examples with three popular IoC containers.

  1. AutoFac
  2. SimpleInjector
  3. Unity

Let’s assume that there is the following class and we want to perform constructor injection with it.

 1public class ConstructorInjectedController
 2{
 3	public IServiceConsumer<IFakeService> FakeService { get; set; }
 4
 5	/// <summary>
 6	/// Initializes a new instance of the <see cref="ConstructorInjectedController"/> class.
 7	/// </summary>
 8	/// <param name="fakeService">The fake service.</param>
 9	public ConstructorInjectedController(IServiceConsumer<IFakeService> fakeService)
10	{
11		this.FakeService = fakeService;
12	}
13}

1. AutoFac

Here is how to use the ServiceConsumerFactory with AutoFac.

1var builder = new Autofac.ContainerBuilder();
2builder.Register(c => ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService")).InstancePerDependency();
3builder.RegisterType<ConstructorInjectedController>();
4
5var autofacContainer = builder.Build();
6
7var controller = autofacContainer.Resolve<ConstructorInjectedController>();

2. SimpleInjector

Here is how to use the ServiceConsumerFactory with SimpleInjector.

1var simpleInjectorContainer = new SimpleInjector.Container();
2simpleInjectorContainer.Register(() => ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService"), SimpleInjector.Lifestyle.Transient);
3
4var controller = simpleInjectorContainer.GetInstance<ConstructorInjectedController>();

3. Unity

And, here is how to use the ServiceConsumerFactory with Unity.

 1var unityContainer = new UnityContainer();
 2
 3unityContainer
 4	.RegisterType<IServiceConsumer<IFakeService>>(
 5		new TransientLifetimeManager(),
 6		new InjectionFactory(c => ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService")))
 7
 8	//
 9	// OR
10	//
11	//.RegisterType<IServiceConsumer<IFakeService>>(
12	//	new InjectionFactory(c => ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient())))
13
14	// 
15	// OR more 'correctly'
16	//
17	//.RegisterType<IFakeService, FakeServiceClient>(new TransientLifetimeManager())
18	//.RegisterType<IServiceConsumer<IFakeService>>(
19	//	new InjectionFactory(c =>
20	//		ServiceConsumerFactory.Create<IFakeService>(() => (ICommunicationObject)c.Resolve<IFakeService>())))
21	;
22
23var controller = unityContainer.Resolve<ConstructorInjectedController>();

Automatic Type Registration

If you decide to automatically register types from all available assemblies, ensure that you exclude the types from the ChannelAdam WCF assembly. For instance, with Unity, note the Where:

1container.RegisterTypes(
2    AllClasses.FromLoadedAssemblies().Where(t => t.Namespace != typeof(IServiceConsumer<>).Namespace), 
3    WithMappings.FromMatchingInterface, 
4    WithName.Default);

If you do not exclude the ChannelAdam.Wcf assembly, the container will likely not be able to automatically resolve IRetryPolicyFunction. If you then register IRetryPolicyFunction manually, the container will continue down a rabbit hole and not be able to resolve System.ServiceModel.ICommunicationObject. Best to not attempt to register types in the ChannelAdam.Wcf library in the first place!

Quick Start

ChannelAdam WCF Library — Version 1 Documentation

Quick Start

To get you started quickly, here is some sample code showing the basic usage.

Install NuGet Package

You can install the ChannelAdam.Wcf NuGet package from within Visual Studio, or find it on the NuGet.org gallery at https://www.nuget.org/packages/ChannelAdam.Wcf.

To install ChannelAdam WCF Library via the Package Manager Console, run the following command:

PM> Install-Package ChannelAdam.Wcf

How To Call a WCF Service

Let’s assume for the duration of this documentation that there is a web service named FakeService and in Visual Studio we have generated a service reference and interface to FakeService, and there is now an IFakeService interface and a FakeServiceClient class to use. That is all normal and basic Visual Studio functionality.

Let’s also assume that the service has the following operation signature:

1int AddIntegers(int first, int second);

Below is sample code for how to use the ChannelAdam WCF Library ServiceConsumer and ServiceConsumerFactory to call the AddIntegers operation.

 1using ChannelAdam.ServiceModel;
 2
 3...
 4
 5//using (var service = ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService"))
 6using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
 7{
 8	try
 9	{
10		int actual = service.Operations.AddIntegers(1, 1);
11
12        ...
13	}
14	// catch (FaultException<MyBusinessLogicType> fe)
15	catch (FaultException fe)
16	{
17		Console.WriteLine("Service operation threw a fault: " + fe.ToString());
18	}
19	catch (Exception ex)
20	{
21		Console.WriteLine("Technical error occurred while calling the service operation: " + ex.ToString());
22	}
23}

Easy and no hassles.

There is no need to catch a CommunicationException or TimeOutException and try to perform the Close/Abort pattern, as the underlying channel is dealt with automatically for you by the ServiceConsumer.

Please see the other documentation pages for further details.

Quick Start

ChannelAdam WCF Library — Version 2 Documentation

Quick Start

To get you started quickly, here is some sample code showing the basic usage.

Install NuGet Package

You can install the ChannelAdam.Wcf NuGet package from within Visual Studio, or find it on the NuGet.org gallery at https://www.nuget.org/packages/ChannelAdam.Wcf.

To install ChannelAdam WCF Library via the Package Manager Console, run the following command:

PM> Install-Package ChannelAdam.Wcf

How To Call a WCF Service

Let’s assume for the duration of this documentation that there is a web service named FakeService and in Visual Studio we have generated a service reference and interface to FakeService, and there is now an IFakeService interface and a FakeServiceClient class to use. That is all normal and basic Visual Studio functionality.

Let’s also assume that the service has the following operation signature:

1int AddIntegers(int first, int second);

Below is sample code for how to use the ChannelAdam WCF Library ServiceConsumer and ServiceConsumerFactory to call the AddIntegers operation.

 1using ChannelAdam.ServiceModel;
 2
 3...
 4
 5//using (var service = ServiceConsumerFactory.Create<IFakeService>("BasicHttpBinding_IFakeService"))
 6using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
 7{
 8	try
 9	{
10		int actual = service.Operations.AddIntegers(1, 1);
11
12        ...
13	}
14	// catch (FaultException<MyBusinessLogicType> fe)
15	catch (FaultException fe)
16	{
17		Console.WriteLine("Service operation threw a fault: " + fe.ToString());
18	}
19	catch (Exception ex)
20	{
21		Console.WriteLine("Technical error occurred while calling the service operation: " + ex.ToString());
22	}
23}

Easy and no hassles.

There is no need to catch a CommunicationException or TimeOutException and try to perform the Close/Abort pattern, as the underlying channel is dealt with automatically for you by the ServiceConsumer.

Please see the other documentation pages for further details.

Release Notes

ChannelAdam WCF Library — Version 2 Documentation

Version 2.1 Release Notes

  • Added: ConsumeAsync() to cater for async Task methods (such as those on a generated service client).
  • Fix: Consume() now synchronously executes a Task if one is returned from the service operation expression so it has control over any exceptions for correct behaviour with the Exception properties.

Version 2 Release Notes

Breaking Changes

  • The retry policy parameter on the Consume() method has been removed.
  • The Exception Behaviour Strategy interface IServiceConsumerExceptionBehaviourStrategy now includes a method named PerformRetryPolicyAttemptExceptionBehaviour. This method is executed when a retry policy is in use and an exception has occurred. When called, this method provides you with the count of the current retry attempt.

New

  • The retry policy is now also applied to the calls made via the Operations property (as well as the Consume() method).
  • Removed dependency on Microsoft Transient Fault Handling Core library.
  • Retry Policy functionality with the Microsoft Transient Fault Handling Core library have been refactored into the NuGet package ChannelAdam.Wcf.Microsoft.Practices.TransientFaultHandling.Core. https://www.nuget.org/packages/ChannelAdam.Wcf.Microsoft.Practices.TransientFaultHandling.Core/

Retry for Transient Fault Handling

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:

  1. Naively - all custom code
  2. With the Microsoft Practices Transient Fault Handling Library; or
  3. 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:

  1. Setting the static property DefaultRetryPolicy on the ServiceConsumerFactory - which will apply to all created instances;
  2. As an overload to the Create method on the ServiceConsumerFactory - which will apply just to that ServiceConsumer instance to be created; or
  3. 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.

Retry for Transient Fault Handling

ChannelAdam WCF Library — Version 2 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 built-in support for plugging in a retry policy.

Please note: unlike version 1, retry policies in version 2, when set, are applied to service calls via both the Operations property and also the Consume method.

Default Retry Policy

Out of the box, no retry policy is applied to service calls. You must specifically set a retry policy for one to be applied.

The Retry Policy Mechanism

A retry policy is implemented as an instance of a class that implements the interface ChannelAdam.TransientFaultHandling.IRetryPolicyFunction (from the ChannelAdam Core NuGet Library). The IRetryPolicyFunction interface contains one simple Execute function, allowing any retry mechanism to be plugged into the ServiceConsumerFactory or ServiceConsumer.

Support For The Microsoft Practices TransientFaultHandling.Core Library

The additional NuGet library ChannelAdam WCF Library Extensions for the Microsoft Practices TransientFaultHandling.Core Library provides extensions to the ChannelAdam WCF Library to support the usage of the Microsoft Practices Transient Fault Handling Library.

How To Set A Retry Policy

There are three ways to set a retry policy:

  1. Setting the static property DefaultRetryPolicy on the ServiceConsumerFactory class - which will then apply to all new created instances of ServiceConsumer;
  2. Specifying the retry policy in one of the Create() method overloads on the ServiceConsumerFactory; or
  3. By setting the RetryPolicy property directly on an instance of ServiceConsumer itself.

You could easily set the static property on the ServiceConsumerFactory in the bootstrap code for your application, and it will apply to all ServiceConsumer service operation calls thereafter. Easy!

Example Usage Of A Retry Policy

This sample code uses the Microsoft Practices Transient Fault Handling Library, supported by the ChannelAdam WCF Library Extensions for the Microsoft Practices TransientFaultHandling.Core Library.

You will notice in this example, I am using the ChannelAdam.ServiceModel.SoapFaultWebServiceTransientErrorDetectionStrategy (from the ChannelAdam.Wcf.Microsoft.Practices.TransientFaultHandling.Core NuGet Library) 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.

The following sample code requires the ChannelAdam.Wcf.Microsoft.Practices.TransientFaultHandling.Core NuGet Library to be installed.

1. Sample Retry Code Using the Operations Property

 1using ChannelAdam.ServiceModel;
 2using Microsoft.Practices.TransientFaultHandling;
 3
 4...
 5
 6var microsoftRetryStrategy = new Incremental(5, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2));
 7var microsoftRetryPolicy = new RetryPolicy<SoapFaultWebServiceTransientErrorDetectionStrategy>(microsoftRetryStrategy);
 8
 9ServiceConsumerFactory.DefaultRetryPolicy = microsoftRetryPolicy.ForServiceConsumer(); // extension method
10// or ServiceConsumerFactory.DefaultRetryPolicy = new ChannelAdam.TransientFaultHandling.RetryPolicyAdapter(microsoftRetryPolicy);
11// or ServiceConsumerFactory.DefaultRetryPolicy = (ChannelAdam.TransientFaultHandling.IRetryPolicyFunction)microsoftRetryPolicy;
12// Because DefaultRetryPolicy has been set, the retry policy will be applied when AddIntegers() is called.
13
14using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
15{
16	try
17	{
18		int actual = service.Operations.AddIntegers(1, 1);
19	}
20	catch (FaultException fe)
21	{
22		Console.WriteLine("Service operation threw a fault: " + fe.ToString());
23	}
24	catch (Exception ex)
25	{
26		Console.WriteLine("Technical error occurred while calling the service operation: " + ex.ToString());
27	}
28}

2. Sample Retry Code Using the Consume Method

The retry policy works exactly the same when the Consume() method is used.

 1using ChannelAdam.ServiceModel;
 2using Microsoft.Practices.TransientFaultHandling;
 3
 4...
 5
 6var microsoftRetryStrategy = new Incremental(5, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2));
 7var microsoftRetryPolicy = new RetryPolicy<SoapFaultWebServiceTransientErrorDetectionStrategy>(microsoftRetryStrategy);
 8
 9ServiceConsumerFactory.DefaultRetryPolicy = microsoftRetryPolicy.ForServiceConsumer(); // extension method
10// or ServiceConsumerFactory.DefaultRetryPolicy = new ChannelAdam.TransientFaultHandling.RetryPolicyAdapter(microsoftRetryPolicy);
11// or ServiceConsumerFactory.DefaultRetryPolicy = (ChannelAdam.TransientFaultHandling.IRetryPolicyFunction)microsoftRetryPolicy;
12// Because DefaultRetryPolicy has been set, the retry policy will be applied when AddIntegers() is called.
13
14using (var service = ServiceConsumerFactory.Create<IFakeService>(() => new FakeServiceClient()))
15{
16	var result = service.Consume(operation => operation.AddIntegers(1, 1));
17
18	if (result.HasNoException)
19	{
20		Console.WriteLine("Actual: " + result.Value);
21		...
22	}
23	else
24	{
25		if (result.HasFaultException)
26		{
27			Console.WriteLine("Service operation threw a fault: " + result.Exception.ToString());
28		}
29		else if (result.HasException)
30		{
31			Console.WriteLine("An unexpected error occurred while calling the service operation: " + result.Exception.ToString());
32		}
33	}
34}

Use Your Own Retry Routine Or Library

An example implementation of IRetryPolicyFunction is the RetryPolicyAdapter in the NuGet library ChannelAdam WCF Library Extensions for the Microsoft Practices TransientFaultHandling.Core Library.

If you want to use some other retry library, simply create your own class that implements IRetryPolicyFunction and set it into the ServiceConsumerFactory or a ServiceConsumer.

Exception Behaviour Strategy For Retry Attempts

The Exception Behaviour Strategy interface IServiceConsumerExceptionBehaviourStrategy includes a method named PerformRetryPolicyAttemptExceptionBehaviour. This method is executed when a retry policy is in use and an exception has occurred. When called, this method provides you with the count of the current retry attempt.