tags:

views:

1851

answers:

4

I use WCF for a client server system. When I add a service reference to IService on the server, a proxy class ServiceClient is generated. My code looks like the following:

ServiceClient client = new ServiceClient();
try
{
    client.Operation1();
}
catch(Exception ex)
{
    // Handle Exception
}
try
{
    client.Operation2();
}
catch(Exception ex)
{
    // Handle Exception
}

The problem is that if there is a communication exception in the first call, the client's state changes to Faulted, and I don't know how to reopen it to make the second call. Is there a way to reopen it? or should I create a new one and replace the instance (It doesn't seem like an elegant way)?

A: 

This is most likely caused by an unhandled exception on the server side. WCF runtime by default terminates your service instance and puts the channel in faulted state in case of unhandled exception and you can no longer communicate over that channel. So you will need to establish a new session with the service. You should catch exceptions on the server side and send the soap faults by raising FaultException or defining FaultContract. There is also returnUnknownExceptionsAsFaults service behavior that you can use.

Mehmet Aras
+5  A: 

Once a an ICommunicationObject (your WCF client object) is in a faulted state, the only way to "re-open" it is to create a new one.

ServiceClient client = new ServiceClient();
try
{
    client.Operation1();
}
catch(Exception ex)
{
    if (client.State == CommunicationState.Faulted)
    {
            client.Abort();
            client = new ServiceClient();
    }
}
try
{
    client.Operation2();
}
catch(Exception ex)
{
   // Handle Exception
}
Mike_G
The problem is that we have an inner function that gets a delegate to a function on a service proxy, and repeatedly executes the function until no communication exception is thrown (our implementation to automatic reconnection). So in this solution, the function will create an instance of the proxy for each execution attempt, and will have to return an updated instance to the caller, so he won't hold a closed proxy... Kind of ugly :-/
Andy
+3  A: 

If there is a communication exception on the first call which is causing a faulted state, you have to basically "re-create" the WCF client proxy. In your example I would probably do something like:

if (client.State == CommunicationState.Faulted)
    client = new ServiceClient();

This would allow you to "re-open" the connection if it is faulted. It may seem a little overkill, but if you're getting a communication exception on the client side, there's probably something else going on (i.e.: server dead? server not responding?)

Good luck

Scott Anderson
+1  A: 

Agree with the last answers, once failed, you need to abort. We use a combination of lambdas and a method like the following to do this:

  public static void Use<TServiceInterface>(TServiceInterface proxy, Action handler)
  {
     Type proxyType = typeof(TServiceInterface);
     IClientChannel channel = (IClientChannel)proxy;

     try
     {
        handler();

        _logSource.Log(LogLevel.Debug, string.Format("Closing client channel for '{0}' ...", proxyType.Name));

        channel.Close();

        _logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' closed.", proxyType.Name));
     }
     catch
     {
        if (channel.State == CommunicationState.Faulted)
        {
           _logSource.Log(LogLevel.Debug, string.Format("Aborting client channel for '{0}' ...", proxyType.Name));

           channel.Abort();

           _logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' aborted.", proxyType.Name));
        }
        else
        {
           _logSource.Log(LogLevel.Debug, string.Format("Closing client channel for '{0}' ...", proxyType.Name));

           channel.Close();

           _logSource.Log(LogLevel.Debug, string.Format("Client channel for '{0}' closed.", proxyType.Name));
        }

        throw;
     }
  }

This is a slight modification of a solution that is already on the .net, but it works great for handling proxies. You then can put multiple service calls in the same lambda expression, and pass it into the method.

In the solution you provided, you always close the channel after the first use, and it might result in bad performance or even wrong functionality when session is used.. It is also kind of strange that a function that doesn't create the channel, is closing it.
Andy
The method above acts like a using statement in C#. You are correct that it calls close after the handler action is executed, but I think what you are missing is that there can be an infinite number of statements in the handler action. You could make 15 calls to the service interface if you wished.Also, I don't believe it's strange at all that the function does not do it all. It is quite common to separate construction and destruction of an object into separate method or separate segments of code. Factories do not typically take responsibility for cleaning up the objects that they create