Reader Level:
ARTICLE

Using the Fault Contracts (SOAP Faults) in WCF Programming

Posted by Sunil Articles | Learn .NET August 26, 2008
This article describes the usage of SOAP Fault Contracts in WCF programming.
  • 0
  • 0
  • 44638
Download Files:
 

 

In this article we are going to see, how to use the Fault contracts in WCF programming.

What is a WCF ?

WCF (Windows Communication Foundation) is a .NET dedicated communication frame work. WCF provides an easy way to expose our methods as services and consuming the existing services as traditional methods. (More about the WCF can be found in my previous article: WCF Programming for Beginners.)

What are SOAP Faults ?

In all managed applications we use the Exception objects to handle the errors. Similarly in WCF programming also to handle the errors and convey that error information from service to client and client to service we use the SOAP faults. FaultContractAttribute, which is defined in the namespace : System.ServiceModel.FaultContractAttribute , is used to declare customized SOAP faults.

SOAP Faults mainly of two types:

  1. Declared SOAP faults. - These are the SOAP faults that are specified on the operations by using the FaultContractAttribute.
     
  2. Undeclared SOAP faults. - These are SOAP faults that occurs in the operations but are not specified on the operations by using the FaultContractAttribute.

[AttributeUsageAttribute(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]

 public sealed class FaultContractAttribute : Attribute

Listing 1: Fault Contract Attribute Syntax 

Creating the Customized SOAP Faults:

To create the customized SOAP faults:

  1. Declare and define the structure for our customized SOAP fault.
  2. Attach the customized SOAP fault to the operations by using the FaultContractAttribute.
  3. Check for the error condition and raise the Customized SOAP fault in service operations.
  4. Handle the SOAP faults at the client side. 

Here I am going to explain all the above steps by using a sample WCF Service.

Sample Program: 

Let us take a WCF service, which has a small function "GetEmpDetails (string EmpID) ". This function accepts empid as input parameter and check for that employee record against the DB. If the employee record exists then it returns that record, if not then it should through a custom fault message to the client, saying that "Employee Record does not exist".

String GetEmpDetails( string EMPID)

{

          // Check For the "EMPID" record in DataBase.

//On Success return the emp details to the client.

//On Failure return the Custom SOAP Fault message to the client.

}

STEP 1: Create the Custom Fault Message Definition.

In this sample program we are going to create custom SOAP Fault that contains an error message string property. 

In WCF we have to use the DataContractAttribute, DataMemberAttribute to expose the properties /variable/ events /indexes from WCF service to clients.

    /// <summary>

    /// This is structure is used to define the custom soap fault message.

    /// Custom SOAP Fault Message class Name :CustomFaultMsg

    /// </summary>

    [DataContract]

    public struct CustomFaultMsg

    {

        /// <summary>

        /// This property is used to pass the custom error information

        /// from service to client.

        /// </summary>

        [DataMember]

        public string MyCustomErrMsg { get; set; }

    }

 

Listing 2: Custom Fault Message Structure

 

STEP 2: Attach the SOAP fault on the operation

 

To make use of the fault messages in our SOAP messages we have attach them to the operations by using "FaultContractAttribute". 

 

    /// <summary>

    /// This is class is exposed as services in WCF programming.

    /// </summary>

    [ServiceContract]

    public interface IService

    {

        /// <summary>

        /// This method is exposed as the operation in WCF.

        /// This method searches for an emp : "empid".

        /// On success it returns the emp details.

        /// On Failure it raises a Fault Message.

        /// </summary>

        /// <param name="EmpId"></param>

        /// <returns></returns>

        [OperationContract]

        [FaultContract(typeof(CustomFaultMsg))]

        Employee GetEmpDetails(string EmpId);

    }

 

"FaultContract[typeof(<<Fault>>)] " attribute is used to add a custom fault to an operation.  In this example, we are adding a custom fault of type "CustomFaultMsg".

"CustomFaultMsg" is structure for our custom fault. (This is defined in step1).

Here function return type "Employee" is an employee structure. "DataContract" attribute is used to expose this structure in service.

    /// <summary>

    /// This Data Contract structure for holding the Employee details.

    ///   </summary>

 

    [DataContract]

   public struct Employee

    {

 

        [DataMember]

        public string FName { get; set; }

 

        [DataMember]

        public string LName { get; set; }

 

        [DataMember]

        public string DeptID { get; set; }

 

        [DataMember]

        public string MailID { get; set; }

 

        [DataMember]

        public string Zip { get; set; }

 

    }

STEP 3: Check for the error condition and raise the customized SOAP fault message: 

In the sample program, IService interface is implemented by a class Service. Here the method GetEmpDetails() should raise our own customized exception, whenever it fails to find the employee details.

        /// <summary>

        /// Search the Data Set for an employee of "EmpID".

        /// If the search is successful, then it returns the employee details object to the client.

        /// On an unsuccessful search it throws the customized fault message to the client.

        /// </summary>

        /// <param name="EmpId">Empid</param>

        /// <returns>Employee Object</returns>

 

        public Employee GetEmpDetails(string EmpId)

        {

            //Search for the "EmpId" employee details in the data set.  By using the LINQ ..

            // Add the Dll reference to DataSetExtensions to support for LINQ

 

            var empRecord = from dr in ds.Tables[0].AsEnumerable()

                                    where dr["EmpId"].ToString() == EmpId

                                    select dr;

 

 

        // If their are no records, then we have to raise our customized SOAP Fault Message.

            if (empRecord.Count() == 0)

            {

                //create an object for the custom SOAP Fault.

                //and initialize the falut with our own customized fault message.

               

  CustomFaultMsg custmErr = new CustomFaultMsg { MyCustomErrMsg = "There is no employee exist with the emp id : " + EmpId };

 

              //Raise the customized fault message exception to the client.

                throw new FaultException<CustomFaultMsg>(custmErr);

 

            }

 

            // if the Emp record is exist , we have to return the emp Details....

 

            else

            {

                // Create an employee object and store all the emp record details in to the

                // employee object.

 

                Employee objEmp = new Employee();

                foreach (DataRow dr in empRecord)

                {

                    // Emp Id is Unique.

                    //So the LINQ select query returns only one row,if the emp exist in the table.

                    objEmp.FName = dr["FirstName"].ToString();

                    objEmp.LName = dr["LastName"].ToString();

                    objEmp.DeptID = dr["DeptId"].ToString();

                    objEmp.MailID = dr["MailId"].ToString();

                    objEmp.Zip = dr["ZipCode"].ToString();

                }

 

                //return the employee object to the client.

                return objEmp;

 

            }

 

        }

 

Listing 3: Sample Program at WCF Service End

 

In this function, the following LINQ technology is used to search for the emp in the existing data set. 

 

var empRecord = from dr in ds.Tables[0].AsEnumerable()

                            where dr["EmpId"].ToString() == EmpId

                            select dr; 

Error Condition: if (empRecord.Count() == 0) is used to throw the custom falut message. Whenever there is an unsuccessful search for the emp, then this condition become true. And here we are going to initialize the object for our customized fault message and throws the exception to the client.

CustomFaultMsg custmErr =

         new CustomFaultMsg { MyCustomErrMsg = "There is no employee exist with the emp id : " + EmpId }; 

 

 throw new FaultException<CustomFaultMsg>(custmErr); 

Host this sample Service on IIS under a virtual directory folder Named "WCFService".  Once this service is hosted, next step is to consume the service by the client.


 

STEP 4 : Handling the  SOAP Fault messages in the client program

Here in the client side while consuming this service use the try and catch blocks to catch the SOAP faults. In Catch blocks , the exception object type should match with our customized SOAP fault created in step1.

Create a client project and add the service reference to our WCFService hosted on IIS:

In the Address: provide the service name, in the name space "WCFServiceProxy" is used.

In the client program, user will enter for an emp id and click on “GetEmpDetails” button. In side this button functionality , we are consuming the service and handling the fault messages:

protected void btnSubmit_Click(object sender, EventArgs e)

        {

            try

            {

                //Initialize an proxy object for the Service.

                WCFServiceProxy.ServiceClient serviceProxy = new ServiceClient();

 

                string strEmpId = txtEmpId.Text.Trim();

                //Consume the WCF Service "GetEmpDetails".

                WCFServiceProxy.Employee objEmp = serviceProxy.GetEmpDetails(strEmpId);

 

                //Add the Employee object to the list, so that it will be displayed on the grid to the user.

                List<Employee> employeeList = new List<Employee>();

                employeeList.Add(objEmp);

 

                //Bind the employee list as a datasource to the datagrid.

                dgEmp.DataSource = employeeList;

                dgEmp.DataBind();

           

            }

            catch (FaultException<CustomFaultMsg> faultMsg)

            {

                //in the faultmsg object , Detail object contains our customized error message information.

                // property "MyCustomErrMsg" is the one we created in our customized fault message description. in ISecrive.cs file.

                string errmsg = faultMsg.Detail.MyCustomErrMsg;

                Response.Write("<Script> alert('" + errmsg + "')</script>");

               

            }

 

        }

 

In the client program Exception object should match with our customized Fault type. 

Catch(FaultException<<Customized Fault Message >> FaultmessageObject) 

The detail object inside the "FaultmessageObject" contains the our customized fault message object. So we used "faultMsg.Detail.MyCustomErrMsg" to get our original customized fault message.

On successful search , the WCF service returns an emp object . This object is add to employee list and displayed to the user in a data grid.

On an unsuccessful search it throws the customized fault message staying that "The emp does not exist".  This SOAP fault is handled and an alert message is displayed.

Using the try catch block at WCF service and throwing the SOAP faults to the client

In the above sample program , emp id is an int type. Now search for an employee id "sa". It throws an exception ,"input string was not in valid format". But in our program , we are not handling these exception. 

In other words, at the service side, if there is any unhandled exception is occurred then those exception details are unavailable at the client side. Due to this, at the client side the following generic error message is displayed. This error message doesn't contain any underlying error/exception details.

In the following code snippet, we are using the try catch blocks at the server side and throw original  Exception details to the client. 

To throw the General Exception, first attach the Fault Contract to the service method.

        [OperationContract]

        [FaultContract(typeof(CustomFaultMsg))]

        [FaultContract(typeof(Exception))]  // newly added for Exception SOAP Fault

      

public Employee GetEmpDetails(string EmpId)

        {

            try

            {

                //Search for the "EmpId" employee details in the data set.  By using the LINQ ..

                // Add the Dll reference to DataSetExtensions to support for LINQ

 

                var empRecord = from dr in ds.Tables[0].AsEnumerable()

                   //this part is same as old program. Main changes are

                    // in Exception blocks…

 

 

                    //return the employee object to the client.

                    return objEmp;

 

                }

            }

            //Catches the Custom Fault message raised in the try block

            catch (FaultException<CustomFaultMsg> custmEx)

            {

                //Throw the customized SOAP fault to the client program.

                throw new FaultException<CustomFaultMsg>(custmEx.Detail,"This is from customized Exception block");

            }

            // To catch any other exception.

            catch (Exception ex)

            {

                //Create the Exception object  and set the exception message .

                Exception GenEx = new Exception(ex.Message);

                //throw the Exception SOAP fault

                throw new FaultException<Exception>(GenEx,"This is from Exception block");

            }

 

       } 

Listing 3: Server Side Code

In this new code main changes are at exception blocks. Catch(Exception ex) is used to handle the all other exceptions. 

At the client Side:

Now we have to handle the General Exception SOAP fault also.

protected void btnSubmit_Click(object sender, EventArgs e)

        {

            try

            {

                //Initialize an proxy object for the Service.

                WCFServiceProxy.ServiceClient serviceProxy = new ServiceClient();

 

                string strEmpId = txtEmpId.Text.Trim();

                //Consume the WCF Service "GetEmpDetails".

                WCFServiceProxy.Employee objEmp = serviceProxy.GetEmpDetails(strEmpId);

 

                //Add the Employee object to the list, so that it will be displayed on the grid to the user.

                List<Employee> employeeList = new List<Employee>();

                employeeList.Add(objEmp);

 

                //Bind the employee list as a datasource to the datagrid.

                dgEmp.DataSource = employeeList;

                dgEmp.DataBind();

 

            }

            catch (FaultException<CustomFaultMsg> faultMsg) //This is used to handle the customized SOAP faults raised by WCFService.

            {

                //in the faultmsg object , Detail object contains our customized error message information.

                // property "MyCustomErrMsg" is the one we created in our customized fault message description. in ISecrive.cs file.

                string errmsg = faultMsg.Detail.MyCustomErrMsg;

                Response.Write("<Script> alert('" + errmsg + "')</script>");

 

            }

            catch (FaultException<Exception> GenEx) // This handle the general Exception SOAP faults raised by WCFService.

            {

                string errmsg = GenEx.Detail.Message;

                Response.Write("<Script> alert('" + errmsg + "')</script>");

            }

            catch (Exception ex) // This is used to handle the exceptions occurred in this client program.

                                // This block does not handles the SOAP faults. This is a Normal catch block.

            {

                string errmsg = ex.Message;

                Response.Write("<Script> alert('" + errmsg + "')</script>");

            }

 

        }

 

Listing 4: Client Program 

In this client program two new exception blocks are added.  

catch (FaultException<Exception> GenEx)

Catch the general Exception raised by the WCF Service (not by the client program.)

catch (Exception ex)

This is a standard catch block which is used to catch the exception raised in the client program.

Now it displays the original error message details occurred at WCF Service program. Sample project code is attached.

COMMENT USING

Trending up