I find it hugely annoying to refresh Service References after every change to service contract. Having said that, if your services are public and you have external consumers for your services you don’t really want to make big breaking changes to your service contract anyway.

But when services are limited to your project or organisation/client you are more likely to change things and maintenance of service references can become a bit tedious then, especially when you have dozens of service references.

But there is an easier way! You can programatically create your service proxies, which, of course, is not without drawbacks as creating a WCF proxy on the fly is quite an expensive operation. The drawbacks can be countered by trying to cache and reuse proxies whenever possible.

And this is what I have done in DynamicWCFProxy library. You can download it from GitHub.

Using it is as easy as this:

using (var client = new ProxyBase<IMyService>())
{
    Console.WriteLine(client.ExecuteProxyFunction(() => client.Proxy.GetData("Dude", 34)));
}

Where IMyService is your WCF service contract and GetData is one of it’s methods. As you can see ProxyBase is the magical thing that manages the lifecycle of the proxy. Proxies are pooled (the maximum number of proxies in pool being limited by the configured number of connections per endpoint) and as soon as we are out of the using block the proxy will be returned to the pool, ready to be used by some other thread. Every proxy has a connection life cycle strategy, which defaults to keep the connection open for a while to avoid reopening it if we do multiple requests to the same service in a sequence.

Now the life cycle strategy might work or not work in your scenario. With WCF you always want to do some performance testing around your app. And it could be that the default life cycle strategy is not your cup of tea, but you can always create your own and then write your code like this instead:

using (var client = new ProxyBase<IMyService>(new MyConnectionLifeCycleStrategy<IMyService>()))
{
    Console.WriteLine(client.ExecuteProxyFunction(() => client.Proxy.GetData("Dude", 34)));
}

MyConnectionLifeCycleStrategy must inherit from ConnectionLifeCycleStrategyBase and the rest is up to you. Let’s have a look at default implementation in the library:

public class DefaultConnectionLifeCycleStrategy<T> : ConnectionLifeCycleStrategyBase<T>
    where T : class 
{
    private readonly object _connectionLock = new object();
    private DateTime _lastUsed;
    private Timer _timer;
    private int? _maxConnectionIdleTime;
    private int MaxConnectionIdleTime
    {
        get
        {
            if (_maxConnectionIdleTime == null)
                // take send timeout (operation timeout) and add 5 seconds on top of it to get the idle time after which connection should be closed
                // this is to avoid closing the connection before it timeouts which could lead to confusing error messages
                _maxConnectionIdleTime = Convert.ToInt32(EndpointContext.ServiceFactory.Endpoint.Binding.SendTimeout.TotalMilliseconds) + 5000;

            return _maxConnectionIdleTime.Value;
        }
    }

    public override T Open()
    {
        lock (_connectionLock)
        {
            var proxyOut = base.Open();

            _lastUsed = DateTime.Now;
            StartConnectionCheck();

            return proxyOut;
        }
    }

    public override void Close()
    {
        lock (_connectionLock)
        {
            base.Close();
            StopConnectionCheck();
        }
    }

    private void StartConnectionCheck()
    {
        if (_timer == null)
            _timer = new Timer(ConnectionCheck, null, MaxConnectionIdleTime, MaxConnectionIdleTime);
        else
            _timer.Change(MaxConnectionIdleTime, MaxConnectionIdleTime);
    }

    private void ConnectionCheck(object state)
    {
        lock (_connectionLock)
        {
            DateTime checkAt = DateTime.Now;
            if ((checkAt - _lastUsed).TotalMilliseconds >= MaxConnectionIdleTime)
            {
                base.Close();
                StopConnectionCheck();
            }
        }
    }

    private void StopConnectionCheck()
    {
        _timer.Change(Timeout.Infinite, Timeout.Infinite);
    }
}

As mentioned, the default strategy is to keep the connection open for as long as possible to avoid having to reopen the connection. This is why in the constructor we read endpoint idle timeout configuration and calculate the max open time based on this. Note that if we have an operation running which is going to timeout we still want to see a timeout exception, hence why max connection open time is 5 seconds longer than configured endpoint idle timeout. Next notice that this strategy keeps track of when the connection was last used which means the connection will remain open for MaxConnectionIdleTime after last call was made, and if a new call is made that gets extended again. A background timer will close the connection if we reach MaxConnectionIdleTime timeout.

Well this is it. I hope you find this useful!