client will not catch generic FaultException< T >, only FaultException

lurscher picture lurscher · Jul 5, 2011 · Viewed 7.3k times · Source

I've read all there is to read on this, but maybe I'm missing something (well, definitely I'm missing something otherwise it would be working already)

I'm throwing some exception error inside my server business layer:

public class RfcException :  Exception
{
   public RfcException(string _m, Exception _inner) : base(_m, _inner)
   { }

   public Dictionary<string, string> ExtendedProperties
   {
      get { return extendedProperties; }
      protected set { extendedProperties = value; }
   }

   private Dictionary<string, string> extendedProperties = new Dictionary<string, string>();
}

which I leave unhandled in the service, but I have an IErrorHandler to catch and create a FaultMessage:

public class FaultErrorHandler : BehaviorExtensionElement, IErrorHandler, IServiceBehavior
{
   public bool HandleError(Exception error)
   {
      if (!Logger.IsLoggingEnabled()) return true;
      var logEntry = new LogEntry
        {
           EventId = 100,
           Severity = TraceEventType.Error,
           Priority = 1,
           Title = "WCF Failure",
           Message = string.Format("Error occurred: {0}", error)
        };
      logEntry.Categories.Add("MiddleTier");

      Logger.Write(logEntry);
      return true;
   }

   public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
   {
      if (error is RfcException)
      {
         RfcException rfcException = error as RfcException;
         var serviceFault = new RfcServiceFault(rfcException);
         var faultException = new FaultException<RfcServiceFault>(serviceFault, new FaultReason(string.Format("System error occurred, exception: {0}", error)));
         var faultMessage = faultException.CreateMessageFault();
         fault = Message.CreateMessage(version, faultMessage, Schema.WebServiceStandard);
      }
      else
      {
         var faultException = new FaultException<Exception>(error, new FaultReason(string.Format("System error occurred, exception: {0}", error)));
         var faultMessage = faultException.CreateMessageFault();
         fault = Message.CreateMessage(version, faultMessage, Schema.WebServiceStandard);
      }
   }

   public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
   { }

   public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
   {
      foreach (ChannelDispatcher chanDisp in serviceHostBase.ChannelDispatchers)
      {
         chanDisp.ErrorHandlers.Add(this);
      };
   }

   public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
   { }

   public override Type BehaviorType
   {
      get { return typeof(FaultErrorHandler); }
   }

   protected override object CreateBehavior()
   {
      return new FaultErrorHandler();
   }
}

No need to ask; I already confirmed with the debugger its entering in the part if (error is RfcException) part, I've stepped through that code and it reach til the end without any trouble. That errorHandler wraps a FaultException<RfcServiceFault> message, the RfcServiceFault message is this

[DataContract(Name = "RfcServiceFault", Namespace = "Service.DataTransfer.Rfc")]
public class RfcServiceFault 
{
   public RfcServiceFault(RfcException rfcException) : this( (Exception)rfcException )
   {
      ExtendedProperties = new Dictionary<string, string>(rfcException.ExtendedProperties);
   }

   public RfcServiceFault()
   { }

   public RfcServiceFault(Exception regularException)
   {
      FaultMessage = regularException.Message;
      StackTrace = regularException.StackTrace;
   }

   public Dictionary<string, string> ExtendedProperties
   {
      get { return extendedProperties; }
      protected set { extendedProperties = value; }
   }

   [DataMember]
   private Dictionary<string, string> extendedProperties = new Dictionary<string, string>();

   [DataMember]
   public string FaultMessage { get; set; }

   [DataMember]
   public string StackTrace { get; set; }
}

The service has all the annotations that should have a wcf service with a faultContract:

[ServiceContract(Name = "MyService", Namespace = Schema.WebServiceStandard, SessionMode = SessionMode.Allowed)]
public interface IMyService 
{
    [OperationContract(Name = "GetStuff")]
    [FaultContract(typeof(RfcServiceFault) , Name="RfcServiceFault", Namespace="Service.DataTransfer.Rfc")]
    LookupResult GetStuff();
}

Now: testing at the client, a simple test like this:

try
{
   var result = myService.GetStuff();
   Assert.IsTrue(!string.IsNullOrEmpty(result));
}
catch (FaultException<RfcServiceFault> rfcEx)
{
   // OMG FOR THE LIFE OF THE PUPPIES ENTER HERE
}
catch (FaultException rr)
{
   // it always falls in here
}
catch (Exception ex)
{ }

I've read, many, many posts about this similar issue:

but nothing so far seems to help, I've tried setting up WCF tracing in the web.config:

<system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing">
        <listeners>
          <add name="log"
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="c:\Traces.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>

and I get a svclog file in there, I open with WCF Trace viewer, but I only see a bunch of messages, the yellow one show the Exception, but it only confirms what the client is already seeing, a System.ServiceModel.FaultException being received, rather than the generic one

Any ideas how to figure this out?

EDIT forgot to mention, i enabled my error handler in config like this:

<behaviorExtensions>
   .....
    <add name="faultErrorHandlerBehavior" 
         type="Service.FaultErrorHandler,Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    .....
</behaviorExtensions>
<serviceBehaviors>
   <behavior name="ServiceBehavior">
       <serviceThrottling maxConcurrentCalls="200" maxConcurrentSessions="200"
                          maxConcurrentInstances="200" />
       <serviceMetadata httpGetEnabled="true" />
       <serviceDebug includeExceptionDetailInFaults="true" />
       <faultErrorHandlerBehavior />
    </behavior>
</serviceBehaviors>

Answer

Phil Degenhardt picture Phil Degenhardt · Jul 6, 2011

(This is a bit of stab in the dark) I believe you may have an issue because the action on the fault does not match that expected at the client. Can you please replace your CreateMessage with the following:

fault = Message.CreateMessage(version, faultMessage, faultException.Action);