client will not catch generic FaultException< T >, only FaultException
Asked Answered
F

1

16

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>
Fanchon answered 5/7, 2011 at 20:13 Comment(7)
i will try commenting the dictionary property from the service faultFanchon
You are trying to solve too many problems at a time. Could you remove your IErrorHandler and simply use some new CustomFault without any additional properties except string?Himes
@Henk, removing the dictionary doesnt seem to improve the situationFanchon
@Henk, after i comment the Dictionary property i'm just left with string datamembers, and i'm not sure how to make the service any more simpler than that. Should the service fault schema be in the service generated wsdl? i don't see it thereFanchon
Create a newservice, new consumer, and new entities in a test project. Start with a basic service method and throw an exception, then change one thing at a time eventually implementing the wall of code you posted above. After every change test your implementation. That should narrow it down. Sorry but just looking at that huge post is making my head hurt.Stun
sorry, but i think that approach will most definitely not work for my problem; the wall of code is taken from an existing project with lots of services, data contracts and different clients which i have to make it work, so even if i get a fully working setup on a test project, how does that help me to figure out the analogous problem in the real project? I would be still left to figure out how to debug the problem in there because whatever i learned from getting it to work in small scale didn't gave any insights about how to isolate the problem in the big oneFanchon
This problem can also occur if you try to use IServiceBehavior/IErrorHandler to translate normal exceptions into FaultExceptions, without using FaultContract on the method that threw the exception. Without [FaultContract(typeof(TDetail))], FaultContract<TDetail> loses its type parameter on the client side.Polypody
D
15

(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);
Dillingham answered 6/7, 2011 at 8:57 Comment(1)
thx for this article,I have fix this problem,by the way ,you need notice : when you add [FaultContract(typeof(RfcServiceFault))] Attribute, you must re-using Service on client sideCoulombe

© 2022 - 2024 — McMap. All rights reserved.