Question

I have recently started to write my own ChannelFactory wrapper to call my WCF service instead of relying on the auto generated code from visual studio

Previously, with the auto generated proxies, I was able to do this for error checking:

    protected AccessControlServiceClient AccessControlService
    {
        get
        {
            // Check if not initialized yet
            if (HttpContext.Current.Session["AccessControlServiceClient"] == null)
                HttpContext.Current.Session["AccessControlServiceClient"] = new AccessControlServiceClient();

            // If current client is 'faulted' (due to some error), create a new instance.
            var client = HttpContext.Current.Session["AccessControlServiceClient"] as AccessControlServiceClient;
            if (client.State == CommunicationState.Faulted)
            {
                try { client.Abort(); }
                catch { /* no action */ }

                client = new AccessControlServiceClient();
                HttpContext.Current.Session["AccessControlServiceClient"] = client;
            }

            return client;
        }
    }

How should I handle this for ChannelFactor? What are the best practices to handle, and re-create the channel should something go wrong? e.g., intermittent network connection, session time out, etc.

This is how my code looks like currently:

Snippet from ProxyBase.cs - creating the channel

private void Initialise()
{
    lock (_sync)
    {
        if (_channel != null) return;

        _channel = new ChannelFactory<T>(_serviceEndPointUri).CreateChannel();
    }
 }

UserManagementServiceClient.cs - IUserManagementService is the WCF contract

public class UserManagementServiceClient : ProxyBase<IUserManagementService>
{
    public UserManagementServiceClient(string serviceEndPointUri)
        : base(serviceEndPointUri)
    {
    }

    public TokenResponse GetToken(TokenRequest request)
    {            
        return Channel.GetToken(request);
    }


    public LoginResponse Login(LoginRequest request)
    {
        return Channel.Login(request);
    }


    public LogoutResponse Logout(LogoutRequest request)
    {
        return Channel.Logout(request);
    }
}

And finally, this is how I'm calling it in my MVC project

    protected UserManagementServiceClient UserManagementService
    {
        get
        {
            // Check if not initialized yet
            if (HttpContext.Current.Session["UserManagementServiceClient"] == null)
                HttpContext.Current.Session["UserManagementServiceClient"] = new UserManagementServiceClient("NetTcpBinding_UserManagementService");

            var client = HttpContext.Current.Session["UserManagementServiceClient"] as UserManagementServiceClient;

            return client;
        }
    }

So presently, whenever the default 10 mins session is up, I get an error because the channel has already closed.

How can I harden my code such that it takes care of network disconnections/session timeouts etc?

Was it helpful?

Solution

I use code like this

public T Channel {
    get {
        lock (_channelLock) {
            if (!object.Equals(_channel, default(T))) {
                if (((ICommunicationObject)_channel).State == CommunicationState.Faulted) {
                    // channel has been faulted, we want to create a new one so clear it
                    _channel = default(T);
                }
            }

            if (object.Equals(_channel, default(T))) {
                // channel is null, create a new one
                Debug.Assert(_channelFactory != null);
                _channel = _channelFactory.CreateChannel();
            }
            return _channel;
        }
   }

OTHER TIPS

I generally use a new service instance per operation. Like this:

public async Task<bool> IsOnline()
    {
        using (var service = new DebugService())
        {
            return await service.OnlineCheckAsync();
        }
    }

Among other things, my wrappers implement the IDisposable following this article. That way you don't need to worry whether the channel is faulted or not every time you perform an operation.

EDIT

The original link for the IDisposable implementation is broken, but the code is shown bellow:

public void Dispose()
    {
        // The following code is from: http://www.vasylevskyi.com/2010/11/correct-wcf-client-proxy-closing.html
        try
        {
            if (this.State != CommunicationState.Closed && this.State != CommunicationState.Faulted)
            {
                ((ICommunicationObject)this).BeginClose(
                    (asr) =>
                    {
                        try
                        {
                            ((ICommunicationObject)this).EndClose(asr);
                        }
                        catch
                        {
                            this.Abort();
                        }
                    }, null
                );
            }
            else
            {
                this.Abort();
            }
        }
        catch (CommunicationException)
        {
            this.Abort();
        }

In my current project, I've created a wrapper class for the client, which uses CurrentInstance accessor model. The getter checks _client.State for Closing, Closed or Faulted and creates a new client in such cases. Creating a new client is done by a factory delegate provided to the wrapper constructor, so that there is a free but consistent way of creating client instances.

For your scenario it would mean storing the wrapper in the Session instead of storing the client directly. Calling wrapper.CurrentInstance would then check for state and re-create the client behind the scenes if a timeout has occured in the meantime.

This model can also be conveniently expanded with robust error handling (for physical disconnect, server crashes...), lazy initialization, arbitrary initialization logic after creating a new client (in my case it's calling sort of Subscribe methods which create a Callback channel, but it can be anything)

I hope this may be the right direction for you as well.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top