Exception Handling + Fault Contract w/ complex data member

Topics: Exception Handling Application Block
Nov 10, 2010 at 7:12 PM

I have been unable to use Enterprise Library 5.0 Exception Handling block with a WCF Fault Contract w/ Complex data member.      If I change the properties of my Fault Contract to use simple data types such as string then it works fine but the mement I try to use a list as a data member, it does not work.

Please see code snippets below.  I would appreciate it anyone can point out what I'm doing wrong or if this is a bug with Enterprise Library 5.0.  

ServiceContract And FaultContract:

[ServiceContract]
    public interface IService1
    {   
        [OperationContract]
        [FaultContract(typeof(SampleFaultContract))]
        string GetDataUsingDataContract(bool test, string str); 
     }

[DataContract]
    public class SampleFaultContract
    {        
        string _stringValue = "Hello";
         List<SAPResponseItem> _responseItems = new List<SAPResponseItem>();
     
        [DataMember]
        public string StringValue
        {
            get { return _stringValue; }
            set { _stringValue = value; }
        }      
        [DataMember]   //THIS IS THE DATAMEMBER THAT I AM UNABLE TO IMPLEMENT WITH ENT LIB 5. 
        public List<SAPResponseItem> ResponseItems
        {
            get { return _responseItems; }
            set { _responseItems = value; }
        }       
    }

    public class SAPResponseItem
    {    
        internal string _responseItemMessage;
        internal string _responseItemMessage2;

        public SAPResponseItem()
        {      }

        [DataMember]
        public string ResponseItemMessage
        {
            get { return _responseItemMessage; }
            set { _responseItemMessage = value; }
        }

        [DataMember]
        public string ResponseItemMessage2
        {
            get { return _responseItemMessage2; }
            set { _responseItemMessage2 = value; }
        }

    }

Service Method:

 public string GetDataUsingDataContract(bool test, string str)
        {
          
            if (string.IsNullOrEmpty(str))
            {
                SampleFaultContract sample = new SampleFaultContract();
                SAPResponseItem item = new SAPResponseItem();

                sample.BoolValue = false;
                sample.StringValue = "error thrown using Fault contract";

                item.ResponseItemMessage = "response item message 1";
                item.ResponseItemMessage2 = "response item message 2";

                sample.ResponseItems.Add(item);
            
                //throw new InvalidOperationException("Error!");

                throw new FaultException<SampleFaultContract>(sample, "Error!");

       
            }
            return "GetDataUsingDataContract";
        }

Service web.Config:

<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="SampleFaultPolicy">
        <exceptionTypes>
          <add name="Exception" type="System.Exception, 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"
                exceptionMessageResourceType="" exceptionMessageResourceName=""
                exceptionMessage="Service error. Please contact your administrator."
                faultContractType="WCFWithEnterpriseLibrary.SampleFaultContract, WCFWithEnterpriseLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                name="Fault Contract Exception Handler">
                <mappings>
                     
                      <add name="StringValue" source="{Message}"/>
                      <add name="ResponseItems" source="{ResponseItems}"/>
                </mappings>
              </add>
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
     
    </exceptionPolicies>
  </exceptionHandling>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

Client Side:

 static void Main(string[] args)
        {
            try
            {
                Service1Client proxy = new Service1Client();

                string test = proxy.GetDataUsingDataContract(true, string.Empty);

              
            }
          

            catch (FaultException<SampleFaultContract> ex)
            {
                SampleFaultContract sFault = ex.Detail;
               
                Console.WriteLine("Fault contract detail: ");
                Console.WriteLine("Fault ID: {0}", sFault.BoolValue);
                Console.WriteLine("Message: {0}", sFault.StringValue);
                Console.WriteLine("ResponseItems: {0}", sFault.ResponseItems[0].ResponseItemMessage.ToString()); //ResponseItems not getting populated
                Console.ReadLine();
            }
           

        }

 

 

Nov 11, 2010 at 1:03 AM

Didn't you use the ExceptionShielding attribute in your service contract (IService1)?  In the case of handling fault exceptions, you need not go through the Fault Contract Exception Handler's mapping process.  The recommend way is to just let those type of exceptions to pass through.  To do this, make sure you decorated your IService1 interface with [ExceptionShielding("SampleFaultPolicy")].  Next,  add the FaultException exception type in your SampleFaultPolicy and set its PostHandlingAction to None.  No need to add any exception handlers.  The addition of this exception type basically tells the EHAB to don't do any wrapping or any processing of FaultExceptions and just throw it back to the client. 

Your SAPResponseItem class is also missing its [DataContract] attribute.

 

Sarah Urmeneta
Global Technologies & Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 16, 2010 at 6:12 PM

Ok. I got it working after removing the Exception handler.   The PostHandlingAction of the new Policy is set to ThrowNewException ( see config below).    Now I wanted to be able to implement Logging for the exception generated and then throw the fault to the client.   How can I do that if I can't use an Exception Handlers provided by ETL 5.   Any ideas?

<add name="NewPolicy">
        <exceptionTypes>
          <add name="FaultException" type="System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException" />
        </exceptionTypes>
      </add>

Nov 16, 2010 at 9:59 PM

Didn't it work if you set the postHandlingAction attribute to None? It should.  If not, could you send a small repro?

<add name="NewPolicy">
        <exceptionTypes>
          <add name="FaultException" type="System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="None" />
        </exceptionTypes>
</add>

Sarah Urmeneta
Global Technologies & Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 17, 2010 at 9:11 AM

Hi Sarah,

I am working with same requirement and ended on this page. I have a question regarding HandlingInstanceId. I will explain my case here.

I am catching all exception in the EHAB and converting them to FaultException<IdMGenericFault>() with following properties. 

FaultId , FaultMessage and FaultTypeId.

PostHandlingAction set to ThrowNewExceptoins and i have mapped the FaultId, FaultMessage and FaultType to Guid, Message and FaultTypeId respectively. I seriously dont know how to get the FaultTypeId in newly thrown exception. 

Do you know how to map the IdMGenericFault property in the newly thrown property ?

I want the GUId and the FaultTypeId and FaultMessage.

Here is the code

 

<mappings>
    <add source="{Guid}" name="FaultID" /> -- I get this from IHAB
    <add source="{Message}" name="FaultMessage" /> -- I get this from FaultExceptoin message property
    <add source="{FaultTypeId}" name="FaultTypeId" /> -- This property is actually in IdMGenericFault. How to map that here.
</mappings>

Thanks in advance for help.

/Rakesh

 

Nov 17, 2010 at 2:30 PM

Hi Sarah:

Regarding your question:  Didn't it work if you set the postHandlingAction attribute to None? It should.  If not, could you send a small repro?

No. It did not work when PostHandlingAction attribute was set to None.     I'll send my code to entlib.support@avanade.com.

I still need to be able to implement Logging.    I want to implement Logging for the exception generated and then throw the fault to the client.   How can I do that if I can't use an Exception Handlers provided by ETL 5.  

Thanks,

Adnan

Nov 17, 2010 at 2:42 PM
Hi Sarah:

Please see attached zip file. I had to set postHandlingAction to ThrowNewException to make the solution work. I also just posted a question on how to implement logging in this solution.

I want to implement Logging for the exception generated and then throw the fault to the client. How can I do that if I can't use an Exception Handlers provided by ETL 5.

Thanks!
Adnan


From: [email removed]
To: [email removed]
Date: Tue, 16 Nov 2010 14:59:41 -0800
Subject: Re: Exception Handling + Fault Contract w/ complex data member [entlib:234212]

From: AvanadeSupport
Didn't it work if you set the postHandlingAction attribute to None? It should. If not, could you send a small repro?
<add name="NewPolicy">
        <exceptionTypes>
          <add name="FaultException" type="System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="None" />
        </exceptionTypes>
</add>

Sarah Urmeneta
Global Technologies & Solutions
Avanade, Inc.
entlib.support@avanade.com
Read the full discussion online.
To add a post to this discussion, reply to this email (entlib@discussions.codeplex.com)
To start a new discussion for this project, email entlib@discussions.codeplex.com
You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.
Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com
Nov 17, 2010 at 2:59 PM

Ok. Sending the attachment did not work.    Pasting my code here.  As I mentioned in my previous post, it works fine.  I just want to be able to implment Logging and am not sure how to proceed.  

Fault Contract:

[ServiceContract]
    public interface IService1
    {


        [OperationContract]
        [FaultContract(typeof(SampleFaultContract))]
        string GetDataUsingDataContract2(bool test, string str);
       
    }


    [DataContract]
    public class SampleFaultContract
    {
        bool boolValue = true;
        string stringValue = "Hello";

        [DataMember]
        public bool BoolValue
        {
            get { return boolValue; }
            set { boolValue = value; }
        }

        [DataMember]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }

        List<SAPResponseItem> _responseItems = new List<SAPResponseItem>();
       
        [DataMember]
        public List<SAPResponseItem> ResponseItems
        {
            get { return _responseItems; }
            set { _responseItems = value; }
        }
       
    }

    [DataContract]
    public class SAPResponseItem
    {
       
        internal string _responseItemMessage;
        internal string _responseItemMessage2;

        public SAPResponseItem()
        {

        }

        [DataMember]
        public string ResponseItemMessage
        {
            get { return _responseItemMessage; }
            set { _responseItemMessage = value; }
        }


        [DataMember]
        public string ResponseItemMessage2
        {
            get { return _responseItemMessage2; }
            set { _responseItemMessage2 = value; }
        }

 

    }

Service Method:

[ExceptionShielding("NewPolicy")]
    public class Service1 : IService1
    {
      
        public string GetDataUsingDataContract2(bool test, string str)
        {
            //SampleFaultContract sample;

            if (string.IsNullOrEmpty(str))
            {
              
                SampleFaultContract sample = new SampleFaultContract();
                SAPResponseItem item = new SAPResponseItem();

                sample.BoolValue = false;
                sample.StringValue = "error thrown using Fault contract";

                item.ResponseItemMessage = "response item message 1";
                item.ResponseItemMessage2 = "response item message 2";

                sample.ResponseItems.Add(item);
            

                throw new FaultException<SampleFaultContract>(sample, "Hard coded Service error. Please contact your administrator.");

       
            }
            return "GetDataUsingDataContract2";
        }
    }

Service web.config:

<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="NewPolicy">
        <exceptionTypes>
          <add name="FaultException" type="System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="ThrowNewException" />
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

Client code:

static void Main(string[] args)
        {
            try
            {
                // Create an instance of the service proxy
                Service1Client proxy = new Service1Client();

                string test = proxy.GetDataUsingDataContract2(true, string.Empty);

              
            }
            catch (Exception ex)
            {
               
                ShowFaultContract(ex);
                Console.ReadLine();

            }
           

        }

        private static void ShowFaultContract(Exception ex)
        {
            var faultContract = ex as FaultException<SampleFaultContract>;
            if (faultContract != null)
            {
               
                SampleFaultContract sFault = faultContract.Detail;
             
            
                Console.WriteLine("Fault contract detail: ");
                Console.WriteLine("Fault ID: {0}", sFault.BoolValue);
                Console.WriteLine("Message: {0}", sFault.StringValue);
                Console.WriteLine("ResponseItems: {0}", sFault.ResponseItems[0].ResponseItemMessage.ToString());
            }
        }

 

Nov 17, 2010 at 11:27 PM

There seems to be a bug.  If you set the postHandlingAction to None, it works as long as you used "WCF Exception Shielding" as the exception policy name.  Using a different name other than the default (WCF Exception Shielding) causes a CommunicationException to be caught at the client and not a FaultException.  Try it out and see if we get the same result.

By the way, it's also a good practice to set the includeExceptionDetailInFaults attribute of the <serviceDebug> element to false if there are information on the original exception which you don't want to show to the client.

 

Sarah Urmeneta
Global Technologies & Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 18, 2010 at 12:29 AM

@Rakesh,

I'm not sure I understand your question. Where would the value of FaultTypeId property come from?  The properties you include in the source attribute must be something that is in every possible exception you're converting to FaultException while the value in the name attribute are those properties in your fault contract.

Please create a new discussion thread for a different topic.

 

Sarah Urmeneta
Global Technologies & Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 18, 2010 at 3:34 PM

Sarah:

Thanks!!!.  It works after I change the policy name to default "WCF Exception Shielding".    Now how can I take it one step further and log this FaultException.     

Nov 18, 2010 at 10:37 PM

Just add a Logging Exception Handler for the FaultException type.

<add name="FaultException" type="System.ServiceModel.FaultException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="None">
                        <exceptionHandlers>
                            <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                                logCategory="General" eventId="100" severity="Error" title="Enterprise Library Exception Handling"
                                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                                priority="0" />
                        </exceptionHandlers>
</add>

Don't forget to reference a logging category in the Category property (logCategory attribute).

 

Sarah Urmeneta
Global Technologies & Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 23, 2010 at 2:34 PM
Edited Nov 23, 2010 at 2:35 PM

That's exactly what I had done i.e added a  Logging Exception handler.  It doesn't work in my solution.   There is no event  in the event log.  Also fault come back as NULL as well.   If I remove logging exception handler, fault comes back just fine.  It appears adding the logging exception handler eats up the fault.

web.config below:

<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="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" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="General">
        <listeners>
          <add name="Event Log 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="WCF Exception Shielding">
        <exceptionTypes>
          <add name="FaultException" type="System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="None">
            <exceptionHandlers>
              <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                logCategory="General" eventId="100" severity="Error" title="Enterprise Library Exception Handling"
                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                priority="0" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

Nov 24, 2010 at 1:54 AM

Set the includeExceptionDetailInFaults to false

<serviceDebug includeExceptionDetailInFaults="false"/>

Sarah Urmeneta
Global Technologies & Solutions
Avanade, Inc.
entlib.support@avanade.com

Nov 24, 2010 at 2:57 PM

Im not sure how this would help to log exceptions since "<serviceDebug includeExceptionDetailInFaults="true"/>" is used to receive exception details in faults for debugging purposes.      Anyways, changing this setting to "false" makes no difference at all.   

I guess Enterprise Library does not make it very easy to log exceptions coming from your fault contract.    I might just have to do it manually without Enterprise Library.

 

 

 

 

Nov 24, 2010 at 10:42 PM

I'm not also sure right now what causes logging to not occur when the includeExceptionDetailInFaults is set to true, that's just what happens when I tried it.  I'm still trying to find out why.  In your case, have you verified that you are able to log to the event viewer?  Try changing your event log trace listener to a flat file trace listener and see if it works.

I also  have the repro project for this, if you want a copy just send me an email at entlib.support@avanade.com.

 

Sarah Urmeneta
Global Technologies & Solutions
Avanade, Inc.
entlib.support@avanade.com