WCF
ChannelFactory
async programming
client-side calls
TModel

How can I implement client-side async calls with WCF's ChannelFactoryT?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

ChannelFactory<T> is useful when you want WCF client control without relying on generated proxy classes. To make client-side calls asynchronous, the cleanest approach is to define async service contracts that return Task or Task<T>, create channels from a reusable factory, and close or abort each channel carefully depending on success or failure.

Start with an Async Contract

The best async client implementation starts with an async service contract.

csharp
1using System.ServiceModel;
2using System.Threading.Tasks;
3
4[ServiceContract]
5public interface IOrderService
6{
7    [OperationContract]
8    Task<OrderDto> GetOrderAsync(int orderId);
9}
10
11public class OrderDto
12{
13    public int Id { get; set; }
14    public string Status { get; set; } = string.Empty;
15}

This is better than wrapping a synchronous call in Task.Run, which only moves blocking work to another thread instead of making the service interaction truly asynchronous.

Reuse the ChannelFactory<T>

The factory itself is relatively expensive to build, so reuse it for a given endpoint configuration and create channels from it as needed.

csharp
1using System;
2using System.ServiceModel;
3
4public sealed class OrderClient : IDisposable
5{
6    private readonly ChannelFactory<IOrderService> _factory;
7
8    public OrderClient(string endpointAddress)
9    {
10        var binding = new BasicHttpBinding
11        {
12            OpenTimeout = TimeSpan.FromSeconds(5),
13            SendTimeout = TimeSpan.FromSeconds(20),
14            ReceiveTimeout = TimeSpan.FromSeconds(20)
15        };
16
17        _factory = new ChannelFactory<IOrderService>(
18            binding,
19            new EndpointAddress(endpointAddress)
20        );
21    }
22
23    public void Dispose()
24    {
25        try
26        {
27            _factory.Close();
28        }
29        catch
30        {
31            _factory.Abort();
32        }
33    }
34}

This keeps factory creation out of the hot path.

Make an Async Client Call

csharp
1using System.ServiceModel;
2using System.Threading.Tasks;
3
4public partial class OrderClient
5{
6    public async Task<OrderDto> GetOrderAsync(int orderId)
7    {
8        var channel = _factory.CreateChannel();
9        var communicationObject = (ICommunicationObject)channel;
10
11        try
12        {
13            var result = await channel.GetOrderAsync(orderId).ConfigureAwait(false);
14            communicationObject.Close();
15            return result;
16        }
17        catch
18        {
19            communicationObject.Abort();
20            throw;
21        }
22    }
23}

The try and catch block matters. If a WCF channel faults, calling Close() can fail. In that case, Abort() is the safe cleanup path.

Use It from Application Code

csharp
1using System;
2using System.Threading.Tasks;
3
4class Program
5{
6    static async Task Main()
7    {
8        using var client = new OrderClient("https://localhost:5001/OrderService.svc");
9
10        try
11        {
12            var order = await client.GetOrderAsync(42);
13            Console.WriteLine($"Order {order.Id}: {order.Status}");
14        }
15        catch (Exception ex)
16        {
17            Console.WriteLine(ex.Message);
18        }
19    }
20}

This keeps the calling thread free while the service call is in flight.

Why Channel Lifecycle Matters

WCF channels are stateful communication objects. A transient failure can move a channel into a faulted state, and once that happens you should not keep reusing it.

That is why a common pattern is:

  • reuse the factory
  • create channels per call or per short-lived scope
  • close on success
  • abort on failure

This is much safer than creating one channel once and keeping it forever.

What If the Service Is Synchronous Only

If you do not control the service contract and it exposes only synchronous operations, true async is limited. You can wrap the call in Task.Run, but that only helps UI responsiveness. It does not improve server scalability or network efficiency the way native async contracts do.

So if you can change the service contract, prefer Task-based operations.

Common Pitfalls

One common mistake is reusing a faulted channel after an exception. WCF channels are not designed for that recovery pattern.

Another issue is rebuilding ChannelFactory<T> for every request. That adds unnecessary overhead.

A third pitfall is calling Close() in all cases, even after a communication fault. Faulted channels often need Abort() instead.

Summary

  • Prefer async WCF service contracts that return Task or Task<T>.
  • Reuse ChannelFactory<T>, but create channels per operation scope.
  • Close successful channels and abort faulted ones.
  • Native async contracts are better than wrapping synchronous calls in Task.Run.
  • Correct channel cleanup is as important as the async call syntax itself.

Course illustration
Course illustration

All Rights Reserved.