The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication

You get this error because you let a .NET exception happen on your server side, and you didn't catch and handle it, and didn't convert it to a SOAP fault, either.

Now since the server side "bombed" out, the WCF runtime has "faulted" the channel - e.g. the communication link between the client and the server is unusable - after all, it looks like your server just blew up, so you cannot communicate with it any more.

So what you need to do is:

  • always catch and handle your server-side errors - do not let .NET exceptions travel from the server to the client - always wrap those into interoperable SOAP faults. Check out the WCF IErrorHandler interface and implement it on the server side

  • if you're about to send a second message onto your channel from the client, make sure the channel is not in the faulted state:

    if(client.InnerChannel.State != System.ServiceModel.CommunicationState.Faulted)
    {
       // call service - everything's fine
    }
    else
    {
       // channel faulted - re-create your client and then try again
    }
    

    If it is, all you can do is dispose of it and re-create the client side proxy again and then try again


To prevent the Server from fall in Fault state, you have to ensure that no unhandled exception is raised. If WCF sees an unexpected Exception, no more calls are accepted - safety first.
Two possibilties to avoid this behaviour:

  1. Use a FaultException (this one is not unexpected for WCF, so the WCF knows that the server has still a valid state)
    instead of

    throw new Exception("Error xy in my function")  
    

    use always

    throw new FaultException("Error xy in my function")  
    

    perhaps you can try..catch the whole block and throw a FaultException in all cases of an Exception

    try   
    {  
        ... some code here   
    }
    catch (Exception ex)
    {  
        throw new FaultException(ex.Message)   
    }
    
  2. Tell WCF to handle all Exceptions using an Errorhandler. This can be done in several ways, I chose a simple one using an Attribute:
    All we have to do more, is to use the attribute [SvcErrorHandlerBehaviour] on the wanted Service Implementation

    using System;
    using System.Collections.ObjectModel;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    
    namespace MainService.Services
    {
        /// <summary>
        /// Provides FaultExceptions for all Methods Calls of a Service that fails with an Exception
        /// </summary>
        public class SvcErrorHandlerBehaviourAttribute : Attribute, IServiceBehavior
        {
            public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            { } //implementation not needed
    
            public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
                                             BindingParameterCollection bindingParameters)
            { } //implementation not needed
    
            public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            {
                foreach (ChannelDispatcherBase chanDispBase in serviceHostBase.ChannelDispatchers)
                {
                    ChannelDispatcher channelDispatcher = chanDispBase as ChannelDispatcher;
                    if (channelDispatcher == null)
                        continue;
                    channelDispatcher.ErrorHandlers.Add(new SvcErrorHandler());
                }
            }
        }
    
        public class SvcErrorHandler: IErrorHandler
        {
            public bool HandleError(Exception error)
            {
                //You can log th message if you want.
                return true;
            }
    
            public void ProvideFault(Exception error, MessageVersion version, ref Message msg)
            {
                if (error is FaultException)
                    return;
    
                FaultException faultException = new FaultException(error.Message);
                MessageFault messageFault = faultException.CreateMessageFault();
                msg = Message.CreateMessage(version, messageFault, faultException.Action);
            }
        }
    }
    

This is an easy example, you can dive deeper into IErrorhandler by not using the naked FaultException, but a FaultException<> with a type that provides additional info see IErrorHandler for an detailed example.


As a matter of fact, if unsuccessful after following suggestions by marc_s, please keep in mind that a <security> element in server binding configuration (or lack thereof) in web.config on the server may cause this exception. For instance the server is expecting Message-level security and client is configured to None (or, if the server is not part of an Active Directory domain but the remote client host is).

Tip: In such cases the client app will most likely invoke the web service fine when executed directly on the server machine under administrative account in RDP session.

Tags:

C#

Wcf