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:
-
Use a FaultException (this one is not unexpected for WCF, so the WCF knows that the server has still a valid state)
instead ofthrow 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) }
-
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 Implementationusing 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.
To diagnose this problem, run the service under the Visual Studio debugger. Use the menu: Debug|Exceptions and indicate that you want to break when an Exception is thrown.
The original exception thrown will have a much better error message than "..it is in the Faulted state."
For example I was getting this exception from ServiceHost.Open(), but when I caught the original exception at the time it was thrown, the error message was:
Service 'MyServiceName' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.
Fixing the spelling error in App.config solved the problem.