/// <summary>
        /// Determines the code for the specified exception, used to uniquely identify the exception.
        /// </summary>
        /// <param name="ex">The exception to be examined in intricate detail with a big magnifying glass.</param>
        /// <returns>The exception code. If the type of exception does not have a code, <c>String.Empty</c> is returned.</returns>
        /// <remarks>
        /// Both <see cref="BusinessProcessException"/> and <see cref="DacSqlException"/> derive from
        /// <see cref="WorkflowException"/> which defines a code for the exception. This makes it very easy to get
        /// hold of the exception code for the specific exception.
        /// </remarks>
        public static string GetExceptionCode(System.Exception ex)
        {
            BusinessProcessException bpex = ex as BusinessProcessException;
            DacSqlException          dex  = ex as DacSqlException;

            if (bpex != null)
            {
                // A workflow engine business process exception
                return(bpex.Code);
            }
            else if (dex != null)
            {
                // A workflow engine data access component (DAC) exception
                return(dex.Code);
            }
            else
            {
                // Codes do not apply to other types of exception, so return String.Empty.
                return(String.Empty);
            }
        }
        /// <summary>
        /// Determines the category of the specified exception, i.e. whether it is a <i>Warning</i> or an <i>Error</i>.
        /// </summary>
        /// <param name="ex">The exception to be examined in intricate detail with a big magnifying glass.</param>
        /// <returns>A value from the <see cref="WorkflowExceptionCategory"/> enumeration.</returns>
        /// <remarks>
        /// Both <see cref="BusinessProcessException"/> and <see cref="DacSqlException"/> derive from
        /// <see cref="WorkflowException"/> which defines a category for the exception. This makes it very easy to get
        /// hold of the exception category for the specific exception.
        /// </remarks>
        public static WorkflowExceptionCategory GetExceptionCategory(System.Exception ex)
        {
            BusinessProcessException bpex = ex as BusinessProcessException;
            DacSqlException          dex  = ex as DacSqlException;

            if (bpex != null)
            {
                // A workflow engine business process exception
                return(bpex.Category);
            }
            else if (dex != null)
            {
                // A workflow engine data access component (DAC) exception
                return(dex.Category);
            }
            else
            {
                // Some other sort of exception, so this must be an error
                return(WorkflowExceptionCategory.Error);
            }
        }
        /// <summary>
        /// Creates a SoapFaultDetail message based upon the type of exception specified.
        /// </summary>
        /// <param name="ex">An exception containing information that is to be added to the SoapFaultDetail message.</param>
        /// <returns>The SoapFaultDetail message.</returns>
        public static XmlDocument CreateSoapFaultDetailMsg(System.Exception ex)
        {
            // Create the message
            SoapFaultDetail msg = new SoapFaultDetail();

            msg.Internal          = new SoapFaultDetailInternal();
            msg.Internal.Workflow = new SoapFaultDetailInternalWorkflow();

            // Set the Message, Exception Number and any other specific information
            BusinessProcessException bpex = ex as BusinessProcessException;
            DacSqlException          dex  = ex as DacSqlException;

            if (bpex != null)
            {
                // A workflow engine business process exception
                HandleBpException(bpex, ref msg);
            }
            else if (dex != null)
            {
                // A workflow engine data access component (DAC) exception
                HandleDacException(dex, ref msg);
            }
            else
            {
                // Handle it as a generic System.Exception
                HandleSystemException(ex, ref msg);
            }

            // Category
            switch (GetExceptionCategory(ex))
            {
            case WorkflowExceptionCategory.Error:
                msg.Category = SoapFaultDetailCategory.Error;
                break;

            case WorkflowExceptionCategory.Warning:
                msg.Category = SoapFaultDetailCategory.Warning;
                break;
            }

            // Exception Case Number
            msg.ExceptionCaseNumber = Framework.ExceptionManagement.ExceptionCaseNumber.Generate();

            // Exception Code
            msg.ExceptionCode = GetExceptionCode(ex);

            // Internal - Subsystem
            msg.Internal.Subsystem = Framework.Subsystem.WorkflowEngine.ToString();

            // Internal - Source
            msg.Internal.Source = ex.Source;

            // Internal - Stack Trace
            msg.Internal.StackTrace = ex.StackTrace.Trim();

            // Internal - Workflow - Workflow Server
            msg.Internal.Workflow.WorkflowServer = System.Environment.MachineName;

            // Internal - Workflow - Orchestration Name
            // Note: We are trying to get information about the orchestration in which the error occurred.
            //       However, if there is a messaging error (e.g. a message could not be delivered to a
            //       SOAP web service, the error will occur in an assembly in the BizTalk Engine, thus the
            //       top-level stack frame will be the BizTalk Engine and not the orchestration.
            //       Also, if there is an error in a component that is called from an Expression shape, the
            //       top-level stack frame will be the component and not the orchestration.
            //       Variable 'sf' will be null if the exception occurred outside of Orchestration
            StackFrame sf = Framework.ExceptionManagement.ExceptionManager.GetStackFrameByNamespace(ex, ".Orchestrations");

            if (sf == null)
            {
                msg.Internal.Workflow.OrchestrationName = "";
            }
            else
            {
                msg.Internal.Workflow.OrchestrationName = sf.GetMethod().DeclaringType.FullName;
            }

            // Serialise the class into Xml and return it as an Xml document
            XmlDocument   msgXml     = new XmlDocument();
            XmlSerializer serializer = new XmlSerializer(typeof(SoapFaultDetail));

            using (System.IO.MemoryStream writer = new System.IO.MemoryStream())
            {
                serializer.Serialize(writer, msg);
                writer.Flush();
                writer.Position = 0;
                msgXml.Load(writer);
            }
            return(msgXml);
        }
 /// <summary>
 /// Handles instances of <see cref="BusinessProcessException"/>.
 /// </summary>
 /// <param name="bpex">The <see cref="BusinessProcessException"/> exception class.</param>
 /// <param name="msg">The Soap Fault Detail message being created.</param>
 private static void HandleBpException(BusinessProcessException bpex, ref SoapFaultDetail msg)
 {
     // Get the error message and business process error number
     msg.Message         = bpex.Message;
     msg.ExceptionNumber = bpex.Number;
 }
 /// <summary>
 /// Handles instances of <see cref="BusinessProcessException"/>.
 /// </summary>
 /// <param name="bpex">The <see cref="BusinessProcessException"/> exception class.</param>
 /// <param name="msg">The Soap Fault Detail message being created.</param>
 private static void HandleBpException(BusinessProcessException bpex, ref SoapFaultDetail msg)
 {
     // Get the error message and business process error number
     msg.Message = bpex.Message;
     msg.ExceptionNumber = bpex.Number;
 }