Confused about the Fault Contract Exception Handler

Topics: Exception Handling Application Block
Jan 27, 2011 at 4:09 PM
Edited Jan 28, 2011 at 12:59 AM

I created a fault contract and modeled it after the SalaryCalculationFault in the EntLib5 Developers Guide. My goal is to have two Exception Types -- ArgumentException and All Exceptions. For the ArgumentException process, I would like to pass the message from the original ArgumentException to the new fault contract Reason/Text element that will contain detail on the argument errors and not log the rethrown message. All other exceptions will log the original exception and then return a generic fault message to the client.

I created a test where the implementation throws an ArgumentException, but can't seem to find a way to return the original message in the newly thrown error... nor is there any useful info in the FaultID and FaultMessage parameters that were suggested in the Guide.


RESULT:

<Fault>
  <Code>
     <Value>Sender</Value>
     <Subcode>
        <Value>Sender</Value>
     </Subcode>
  </Code>
  <Reason>
     <Text xml:lang="en-US">Invalid arguments in service call.</Text>
  </Reason>
  <Detail>
     <ServiceFault>
       <FaultID>00000000-0000-0000-0000-000000000000</FaultID>
       <FaultMessage i:nil="true"/>
     </ServiceFault>
  </Detail>
</Fault>


 

SERVICE FAULT CONTRACT:

namespace MyCompany.MobileApps.Domain.Entities
{
    [DataContract]
    public class ServiceFault
    {
        [DataMember]
        public Guid FaultID { get; set; }

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

WCF TEST SERVICE:

namespace MyCompany.MobileApps.DistributedServices
{
    ////[ServiceAuthentication]
    [ServiceConfiguration("DeviceIdRestBehavior", true)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    [ExceptionShielding("WCFExceptionShielding")]
    public class SystemSvc : PublicAppBase, ISystem
    {
        [WebDispatchFormatter]
        [WebHelp(Comment = "Get Call Center Phone Number")]
        [WebGet(UriTemplate = "GetCallCenterPhoneNum?location={location}")]
        public string GetCallCenterPhoneNum(string location)
        {
            return impl.GetCallCenterPhoneNum("North Pole");
        }
    }
}
NOTE: There is no call center at the North Pole, so the impl.GetCallCenterPhoneNum implementation throws an ArgumentException that should be processed per the ExceptionShielding("WCFExceptionShielding")] policy.


web.config from EntLib5 config utility:
<configuration>

  <configSections>
    <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
    <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
  </configSections>


  <loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
    <listeners>
      <add name="Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        fileName="TraceLogs\MobileAppTrace.log" footer="" formatter="Text Formatter" />
      <add name="Event Log Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        source="Enterprise Library Logging" formatter="Text Formatter"
        log="" machineName="." traceOutputOptions="None" />
    </listeners>
    <formatters>
      <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        template="Timestamp: {timestamp}{newline}&#xA;Message: {message}{newline}&#xA;Category: {category}{newline}&#xA;Priority: {priority}{newline}&#xA;EventId: {eventid}{newline}&#xA;Severity: {severity}{newline}&#xA;Title:{title}{newline}&#xA;Machine: {localMachine}{newline}&#xA;App Domain: {localAppDomain}{newline}&#xA;ProcessId: {localProcessId}{newline}&#xA;Process Name: {localProcessName}{newline}&#xA;Thread Name: {threadName}{newline}&#xA;Win32 ThreadId:{win32ThreadId}{newline}&#xA;Extended Properties: {dictionary({key} - {value}{newline})}"
        name="Text Formatter" />
      <add prefix="x" namespace="MyCompany.MobileApps" type="MyCompany.MobileApps.Infrastructure.Crosscutting.XmlFormatter, MyCompany.MobileApps.Infrastructure.Crosscutting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        name="MyCompany XML Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="ActivityTracing" name="Trace">
        <listeners>
          <add name="Flat File Trace Listener" />
        </listeners>
      </add>
      <add switchValue="All" name="General">
        <listeners>
          <add name="Flat File Trace Listener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events" />
      <notProcessed switchValue="All" name="Unprocessed Category" />
      <errors switchValue="All" name="Logging Errors &amp; Warnings">
        <listeners>
          <add name="Event Log Listener" />
        </listeners>
      </errors>
    </specialSources>
  </loggingConfiguration>
  <exceptionHandling>
    <exceptionPolicies>
      <add name="WCFExceptionShielding">
        <exceptionTypes>
          <add name="ArgumentException" type="System.ArgumentException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                exceptionMessage="Invalid arguments in service call." faultContractType="MyCompany.MobileApps.Domain.Entities.ServiceFault, MyCompany.MobileApps.Domain.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Fault Contract Exception Handler">
                <mappings>
                  <add source="[Guid]" name="FaultId" />
                  <add source="[Message]" name="FaultMessage" />
                </mappings>
              </add>
            </exceptionHandlers>
          </add>
          <add name="All Exceptions" type="System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="NotifyRethrow" />
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>

[...]

</configuration>


 

Jan 27, 2011 at 10:09 PM
Edited Jan 28, 2011 at 12:55 AM

Here is another minimal test trying to use a Fault Contract that is not giving the result expected from the EntLib5 Developer's Guide (Message not conforming to template in web.config file).

 


 

RESULT:

 

Unhandled Exception: System.ServiceModel.FaultException: Testing Fault Contract
Exception Handler. ERROR ID: [handlingInstanceID]

Server stack trace:
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRunt
ime operation, ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan tim
eout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCall
Message methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req
Msg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgDa
ta, Int32 type)
   at WctTest.ServiceReference1.IService1.GetData(String name)
   at WctTest.ServiceReference1.Service1Client.GetData(String name) in C:\Users\
v.Bill_Mattox\Documents\Visual Studio 2010\Projects\WcfService1\WctTest\Service
References\ServiceReference1\Reference.cs:line 50
   at WctTest.Program.Main(String[] args) in C:\Users\v.Bill_Mattox\Documents\Vi
sual Studio 2010\Projects\WcfService1\WctTest\Program.cs:line 14
Press any key to continue . . .

 


 

CODE:

 

namespace WcfService1
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData(string name);
    }
}

namespace WcfService1
{
    [ExceptionShielding("ExceptionShielding")]
    public class Service1 : IService1
    {
        protected ExceptionManager ExceptionMgr;

        public Service1()
        {
            var IoC = new UnityContainer().AddNewExtension<EnterpriseLibraryCoreExtension>();
            ExceptionMgr = IoC.Resolve<ExceptionManager>();
        }

        public string GetData(string name)
        {
            throw new ArgumentException("Testing argument Exception");
            return "Hello " + name;
        }
    }
}
namespace WcfService1
{
    [DataContract]
    public class ServiceFault
    {
        [DataMember]
        public Guid FaultID { get; set; }

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

 

WEB.CONFIG

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
  </configSections>
  <exceptionHandling>
    <exceptionPolicies>
      <add name="WcfExceptionShielding">
        <exceptionTypes>
          <add name="All Exceptions" type="System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException">
            <exceptionHandlers>
              <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                exceptionMessage="Testing Fault Contract Exception Handler. ERROR ID: [handlingInstanceID]"
                faultContractType="WcfService1.ServiceFault, WcfService1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c053fc02bf54dd20"
                name="Fault Contract Exception Handler">
                <mappings>
                  <add source="[handlingInstanceID]" name="FaultID" />
                  <add source="[Message]" name="FaultMessage" />
                </mappings>
              </add>
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

Jan 28, 2011 at 8:15 AM

It looks like the square bracket is the one causing the property mapping not to work. Try the following configuration below and see if this resolves the issue.

Just ignore unnecessary items you don't need (i.e.faultType, etc..), but take note of the token/source name and format.

        <exceptionPolicies>
            <add name="WCF Exception Shielding">
                <exceptionTypes>
                    <add name="DivideByZeroException" type="System.DivideByZeroException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
                        postHandlingAction="ThrowNewException">
                        <exceptionHandlers>
                            <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                                exceptionMessage="Testing Fault Contract Exception Handler. ERROR ID: {handlingInstanceID}"
                                faultContractType="MathService.NumberFault, MathService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                                name="Fault Contract Exception Handler">
                                <mappings>
                                    <add source="handlingInstanceID" name="FaultID" />
                                    <add source="Message" name="FaultMessage" />
                                    <add source="StackTrace" name="StackTrace" />
                                    <add source="Data" name="FaultData" />
                                </mappings>
                            </add>
                        </exceptionHandlers>
                    </add>
                </exceptionTypes>
            </add>
        </exceptionPolicies>

 

Gino Terrado
Global Technologies and Solutions
Avanade, Inc.
entlib.support@avanade.com

Jan 28, 2011 at 4:12 PM

I am getting closer. The image in the developer guide pdf was blurry and looked like "[]" rather than "{}". I created a new fault contract that included the original exception Data property and no stacktrace, which I do not want to send to the client. However, my test is now showing the correct exception message after rethrowing, the mappings are not being thrown and I am still getting a stacktrace.


RESULT:

Unhandled Exception: System.ServiceModel.FaultException: Testing Fault Tolerant
Exception Handler.

ERROR ID: b642ef59-a526-4057-a8b7-315f6aac926f

Server stack trace:
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRunt
ime operation, ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan tim
eout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCall
Message methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req
Msg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgDa
ta, Int32 type)
   at WcfTest.ServiceReference1.IService1.GetData(String name)
   at WcfTest.ServiceReference1.Service1Client.GetData(String name) in C:\Users\
Bill\Documents\Visual Studio 2010\Projects\WcfService1\WcfTest\Service
References\ServiceReference1\Reference.cs:line 50
   at WcfTest.Program.Main(String[] args) in C:\Users\Bill\Documents\Vi
sual Studio 2010\Projects\WcfService1\WcfTest\Program.cs:line 14
Press any key to continue . . .

FAULT CONTRACT
namespace WcfService1
{
    [DataContract]
    public class ServiceFault
    {
        [DataMember]
        public Guid FaultID { get; set; }

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

        [DataMember]
        public IDictionary<object, object> Data { get; set; }
    }
}


WEB.CONFIG

<exceptionPolicies>
  <add name="WCF Exception Shielding">
    <exceptionTypes>
      <add name="All Exceptions" type="System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
        postHandlingAction="ThrowNewException">
        <exceptionHandlers>
          <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            exceptionMessage="Testing Fault Tolerant Exception Handler.&#xD;&#xA;&#xD;&#xA;ERROR ID: {handlingInstanceID}"
            faultContractType="WcfService1.ServiceFault, WcfService1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c053fc02bf54dd20"
            name="Fault Contract Exception Handler">
            <mappings>
              <add source="handlingInstanceID" name="FaultID" />
              <add source="Message" name="FaultMessage" />
              <add source="Data" name="FaultData" />
            </mappings>
          </add>
        </exceptionHandlers>
      </add>
    </exceptionTypes>
  </add>
</exceptionPolicies>

Jan 28, 2011 at 5:07 PM

Found another discovery. "handleInstanceID" needs to be "Guid" in the mappings. I am still unable to return Data, even though I have added items to it.

<exceptionPolicies>
  <add name="WCF Exception Shielding">
    <exceptionTypes>
      <add name="All Exceptions" type="System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
        postHandlingAction="ThrowNewException">
        <exceptionHandlers>
          <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            exceptionMessage="Testing Fault Tolerant Exception Handler.&#xD;&#xA;&#xD;&#xA;ERROR ID: {handlingInstanceID}"
            faultContractType="WcfService1.ServiceFault, WcfService1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c053fc02bf54dd20"
            name="Fault Contract Exception Handler">
            <mappings>
              <add source="Guid" name="FaultID" />
              <add source="Message" name="FaultMessage" />
              <add source="Data" name="FaultData" />
            </mappings>
          </add>
        </exceptionHandlers>
      </add>
    </exceptionTypes>
  </add>
</exceptionPolicies>

Jan 28, 2011 at 5:51 PM

Found the problem with Data. Needed to define the contract as follows:

namespace Domain.Entities
{
    [DataContract]
    public class ServiceFault
    {
        [DataMember]
        public Guid FaultID { get; set; }

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

        [DataMember]
        public System.Collections.IDictionary FaultData { get; set; }
    }
}