Replace Fault with exception with original errormessage

Topics: Exception Handling Application Block
Nov 24, 2011 at 12:17 PM

Hello,

We have a servicelayer and a client application. In the servicelayer I have ExceptionShielding that returns Faults with the FaultContractExceptionHandler. We get the Fault with message from the server. There we want to convert the fault back to an exception with the original message. We use the replacehandler to do this. The problem is that we won't get the original message from the Fault. The logging that happens before the replacement in the app.config is seeing the original message and I also see it when I debug the application. This is what we have:

Web.config


<add name="OrderLineForArticleNumberNotExistsException" type="Common.Core.OrderLineForArticleNumberNotExistsException, Common.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                      postHandlingAction="ThrowNewException">
                        <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="Default Category" eventId="5008" severity="Warning"
                              title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                              priority="0" />
                            <add type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                              exceptionMessage="" faultContractType="ServiceContracts.Faults.ServiceOrderLineForArticleNumberNotExistsFault, ServiceContracts.Faults, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
                              name="Fault Contract Exception Handler">
                                <mappings>
                                    <add source="{Message}" name="MessageText" />
                                    <add source="{Guid}" name="Id" />
                                </mappings>
                            </add>
                        </exceptionHandlers>
                    </add>
App.Config
<add name="ServiceOrderLineForArticleNumberNotExistsFault" type="System.ServiceModel.FaultException`1[[ServiceContracts.Faults.ServiceOrderLineForArticleNumberNotExistsFault, ServiceContracts.Faults, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
                      postHandlingAction="ThrowNewException">
                        <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="Default Category" eventId="5008" severity="Warning"
                              title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
                              priority="0" />
                            
Here should the right replacer come right????

                        </exceptionHandlers>
                    </add>
I tryed several things such as:

<

 

add name="Replace Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

"

 

 

exceptionMessage="The error is - {MessageText}" replaceExceptionType="Centric.Its.Common.Core.OrderLineForArticleNumberNotExistsException, Centric.Its.Common.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

/>

Fault:
/// <summary>
    /// This fault is thrown when the orderline for the articlenumber does not exist.
    /// See messagetext for more information.
    /// </summary>
    [DataContract]
    public class  ServiceOrderLineForArticleNumberNotExistsFault 
    {
        #region Properties

        [DataMember]
        public Guid Id { get; set; }

        [DataMember]
        public String MessageText { get; set; }

        #endregion

        #region Ctor

        public ServiceOrderLineForArticleNumberNotExistsFault()
        {

        }

        #endregion
    }

We use Entlib 5.
Nov 24, 2011 at 10:51 PM

If I understand you correctly, you want to take a FaultContract<> and be able to use Enterprise Library to convert it into your own custom exception class
containing the information from the original fault.  Sort of a reverse WCF.FaultContractExceptionHandler.

I don't think you can do that out of the box. You could write a custom ExceptionHandler to do it, though.

This is a simple handler that takes a replaceType Exception, a defaultMessage in case the exception type is not what was expected.  The rest of the parameters are used
as constructor arguments to the "replaceType" Exception (passed in the order they appear) .  If it doesn't suit you 100% you can use it as a starting point:

    /// <summary>
    /// Replaces the exception in the chain of handlers with a cleansed exception.
    /// </summary>
    [ConfigurationElementType(typeof(CustomHandlerData))]
    public class FaultExceptionCustomReplaceHandler : IExceptionHandler
    {
        private readonly Type replaceExceptionType;
        List<string> constructorArgNames = new List<string>();
        private string defaultMessage;

        /// <summary>
        /// Initializes a new instance of the <see cref="FaultExceptionCustomReplaceHandler"/> class with an exception message and the type of <see cref="Exception"/> to use.
        /// </summary>
        /// <param name="exceptionMessage">The exception message.</param>
        /// <param name="replaceExceptionType">The type of <see cref="Exception"/> to use to replace.</param>
        public FaultExceptionCustomReplaceHandler(NameValueCollection settings)
        {
            foreach (var key in settings.AllKeys)
            {
                if (string.Compare(key, "replaceType", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    replaceExceptionType = Type.GetType(settings[key]);
                }
                if (string.Compare(key, "defaultMessage", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    defaultMessage = settings[key];
                }
                else
                {
                    constructorArgNames.Add(settings[key]);
                }
            }
        }

        /// <summary>
        /// The type of exception to replace.
        /// </summary>
        public Type ReplaceExceptionType
        {
            get { return replaceExceptionType; }
        }

        /// <summary>
        /// Replaces the exception with the configured type for the specified policy.
        /// </summary>
        /// <param name="exception">The original exception.</param>        
        /// <param name="handlingInstanceId">The unique identifier attached to the handling chain for this handling instance.</param>
        /// <returns>Modified exception to pass to the next exceptionHandlerData in the chain.</returns>
        public Exception HandleException(Exception exception, Guid handlingInstanceId)
        {
            return ReplaceException(exception);
        }

        /// <summary>
        /// Replaces an exception with a new exception of a specified type.
        /// </summary>                
        /// <param name="replaceExceptionMessage">The message for the new exception.</param>
        /// <returns>The replaced or "cleansed" exception.  Returns null if unable to replace the exception.</returns>
        private Exception ReplaceException(Exception exception)
        {
            Type exceptionType = exception.GetType();

            if (exception is FaultException && exceptionType.IsGenericType)
            {
                Type instanceGenericType = exceptionType.GetGenericArguments()[0];

                var dataProperty = exceptionType.GetProperty("Detail");
                object detail = dataProperty.GetValue(exception, null);

                List<object> args = new List<object>();

                foreach (var propertyName in constructorArgNames)
                {
                    var property = detail.GetType().GetProperty(propertyName);

                    if (property == null)
                    {
                        throw new Exception(
                            string.Format("Configured Property Name {0} does not exist in type {1}.  Verify that the property exists (check spelling and case).",
                                propertyName, instanceGenericType.FullName));
                    }

                    args.Add(property.GetValue(detail, null));
                }

                return (Exception)Activator.CreateInstance(replaceExceptionType, args.ToArray());
            }

            return (Exception)Activator.CreateInstance(replaceExceptionType, new object[] { defaultMessage ?? exception.Message});
        }
    }
}

Then you can use it like so:

    public class MyException : Exception
    {
        public MyException()
            : base()
        {
        }

        public MyException(string message)
            : base(message)
        {
        }

        public MyException(string message, string Id)
            : base(message)
        {
        }
    }

    class MyFault
    {
        public string ID { get; set; }
        public string MessageText { get; set; }
    }

    class Program
    {
        static void ThrowException()
        {
            MyFault itsYourFault = new MyFault()
            {
                ID = Guid.NewGuid().ToString(),
                MessageText = "Arg!  Bad Operation, matey"
            };
            throw new FaultException<MyFault>(itsYourFault);
        }

        static void Main(string[] args)
        {
            try
            {
                var builder = new ConfigurationSourceBuilder();
                builder.ConfigureExceptionHandling()
                    .GivenPolicyWithName("MyFault")
                     .ForExceptionType<FaultException<MyFault>>()
                     .HandleCustom(typeof(FaultExceptionCustomReplaceHandler), 
                            new NameValueCollection() 
                            { 
                                { "defaultMessage", "An error occurred." },
                                { "replaceType", "Ehab.MyException, Ehab" },
                                { "constructorArg1", "MessageText" }, 
                                { "constructorArg2", "ID" } 
                            }
                        )
                       .ThenThrowNewException()
                    ;

                var configSource = new DictionaryConfigurationSource();
                builder.UpdateConfigurationWithReplace(configSource);
                EnterpriseLibraryContainer.Current
                  = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);

                ThrowException();
            }
            catch (FaultException<MyFault> fault)
            {
                var exceptionManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();                
                exceptionManager.HandleException(fault, "MyFault");
            }
        }
    }

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com