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>
They're not same. In a WCF service, if it throws an exception inside the service, the client will not get the details. In order to get the formatted exception details on client side, you need to use FaultException instead to let the client know the details. The FaultException information can be serialized as expected.
A Fault Contract is a way to handle an error/exception in WCF. In C# we can handle the error using try and catch blocks at the client-side. The purpose of a Fault Contract is to handle an error by the service class and display in the client-side.
(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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With