Throwing and catching Strongly Types SOAP Faults in WCF

It’s common scenario, a call to WCF service to fail. The service call can fail for thousands of reasons such as network problems, database crushes or even runtime logic errors. In these cases, the client called the service would appreciate if he knew the reason why the call failed and maybe he could take some action to handle it’s function respectively. WCF library provides the FaultException class from System.ServiceModel namespace that can handle that kind of failures. When the service detect an error, a FaultException is raised and can be sent back to the client, providing him with several information. This article will give you an example of how to implement this functionality. We will modify the project in this post.
solution

First we are going to create the Fault DataContracts, the Fault type of exception that is going to be raised and return back to the client. In the “AdventureWorksService” project, where the service is implemented, open the IAdventureWorksService and add two Fault Contracts. As you will see, these classes are just like the ProductData class, that is are amended with the attributes [DataContract] for the class plus the [DataMember] for their properties. The reason is very simple. The information of these classes must be serializable so it can be sent back to client. We are going to add two types of Faults. One for Logic WCF exceptions and one for Database exception. The code for the IAdventureSowrksService.cs file is this.

namespace AdventureWorks
{
    // Data contract describing the details of a product passed to client applications
    [DataContract]
    public class ProductData
    {
        [DataMember]
        public string Name;
        [DataMember]
        public string ProductNumber;
        [DataMember]
        public string Color;
        [DataMember]
        public decimal ListPrice;
    }

    // Fault Contracts
    [DataContract]
    public class LogicFault
    {
        [DataMember]
        public string Reason { get; set; }
        [DataMember]
        public string Message { get; set; }
    }
    [DataContract]
    public class DatabaseFault
    {
        [DataMember]
        public string Reason { get; set; }
        [DataMember]
        public string Message { get; set; }
    }

    // Service contract describing the operations provided by the WCF service
    [ServiceContract]
    public interface AdventureWorksService
    {
        // Get the product number of every product
        [FaultContract(typeof(LogicFault))]
        [FaultContract(typeof(DatabaseFault))]
        [OperationContract]
        List<string> ListProducts();
        // Get the details of a single product
        [OperationContract]
        ProductData GetProduct(string productNumber);
    }
}

We need to enable  ListProducts() operation to generate SOAP Faults so we simply added two [FaultContract] attributes before the operation’s declaration.

Edit the AdventureWorksService.cs code to catch an Exception and if so, check the type: If it is an Sql.Exception then throw a DatabaseFault FaultException else throw a LogicFault FaultException. The DatabaseFault exception occurs in cases such as database connection rejections and LogicFault Exceptions occurs in cases such as when try to iterate a List object while it is null. We ‘ll see how this work later.

namespace AdventureWorks
{
    public class AdventureWorksServiceImpl : AdventureWorksService
    {
        public List<string> ListProducts()
        {
            // Create a list for holding product numbers
            List<string> productsList = new List<string>();
            try
            {
                // Connect to the AdventureWorks database by using the Entity Framework
                using (AdventureWorks2012Entities database = new AdventureWorks2012Entities())
                {
                    // Fetch the product number of every product in the database
                    var products = from product in database.Products
                                   select product.ProductNumber;
                    productsList = products.ToList();
                }
            }
            catch(Exception e)
            {
                if (e.InnerException is System.Data.SqlClient.SqlException)
                {
                    DatabaseFault fault = new DatabaseFault
                    {
                        Reason = "Exception accessing database",
                        Message = e.InnerException.Message
                    };
                    throw new FaultException<DatabaseFault>(fault);
                }
                else
                {
                    LogicFault fault = new LogicFault
                    {
                        Reason = "Exception retrieving products",
                        Message = e.Message
                    };
                    throw new FaultException<LogicFault>(fault);
                }
            }
            // Return the list of product numbers
            return productsList;
        }
        //GetProduct code ommitted
    }
}

Build the solution. The current website hosted in IIS has the old implementation so we need to re-publish the service site again.Right click the AdventureWorksService in the solution, select Publish Website, select Local IIS and put the same address you had put in the previous
posts (http://localhost/AdventureWorksService). If asked to replace existing files, just confirm. The service should run in the same way but with new service implementation at this time.

In the AdventureWorksClient project, right click “AdventureWorksService” service file in the Service References folder, and select “update service reference”. The proxy will now be based on the new service implementation and the client should be able to catch Fault Exceptions. Modify the main method in the Program.cs file to catch Fault Exceptions as follows.

namespace AdventureWorksClient
{
class Program
{
    static void Main(string[] args)
    {
// Create a proxy object and connect to the service
        AdventureWorksServiceClient proxy = new AdventureWorksServiceClient("BasicHttpBinding_AdventureWorksService");
        try
        {
// Obtain a list of all products
            Console.WriteLine("Test 1: List all products");
            string[] productNumbers = proxy.ListProducts();
            foreach (string productNumber in productNumbers)
            {
                Console.WriteLine("Number: {0}", productNumber);
            }
            Console.WriteLine();

            Console.WriteLine("Test 2: Display the details of a product");
            ProductData product = proxy.GetProduct("SA-M198");
            Console.WriteLine("Number: {0}", product.ProductNumber);
            Console.WriteLine("Name: {0}", product.Name);
            Console.WriteLine("Color: {0}", product.Color);
            Console.WriteLine("Price: {0}", product.ListPrice);
            Console.WriteLine();

// Disconnect from the service
            proxy.Close();
        }
        catch (FaultException<LogicFault> sf)
        {
            Console.WriteLine("SystemFault {0}: {1}\n", sf.Detail.Message,sf.Detail.Reason);
        }
        catch (FaultException<DatabaseFault> dbf)
        {
            Console.WriteLine("DatabaseFault {0}: {1}\n", dbf.Detail.Message, dbf.Detail.Reason);
        }
        Console.WriteLine("Press ENTER to finish");
        Console.ReadLine();
    }
}

}

Build solution, set client’s project as the startup and run it. Is should run without errors. In order to test the Database FaultException, simply change the connection string element in the Web.config file, of the AdventureWorkService website hosted in IIS. Note that you should change the file in the “C:\inetpub\wwwroot\AdventureWorksService” location, not the one in you Visual Studio! Change the Initial Catalog attribute to point to a fake database that it doesn’t exist. (If IIS doesn’t allow you to change the attribute because apparently it’s being used at the moment, just cut the Web.config file, paste it in your desktop folder, make the above change and put it back where it was.


initial catalog=failDB;

Try to run again the client project. You should take a message like “System cannot open database “failDb” requested by login”. A DatabaseFault exception has been caught.

dbexception

Change again the Web.config file and make it to point the right database. You can test the LogicFault exception by modifying the AdventureWorksService.cs file, where the implementation of the service exist. Simply modify the first line of the ListProducts() operation by assigning the ProductList to null. Also later, try to clear the list items. The clear method, should fail (list is null) and an Exception will raise. This time though, it isn’t an Sql.Exception so a LogicFault exception will be through.


public List<string> ListProducts()
{
// Create a list for holding product numbers
List<string> productsList = null;
try
{
// Connect to the AdventureWorks database by using the Entity Framework
using (AdventureWorks2012Entities database = new AdventureWorks2012Entities())
{
// Fetch the product number of every product in the database
var products = from product in database.Products
select product.ProductNumber;
productsList.Clear();
productsList = products.ToList();
}
}
//Other code omitted

Build web site, re-publish it in IIS and run the client project again. This time, you should get a LogicFault exception.

Advertisements


Categories: WCF

Tags:

1 reply

Trackbacks

  1. Distributed Transactions in WCF services – Part 1 | chsakell's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Chara Plessa

The purpose of this blog is to broaden my education, promote experimentation and enhance my professional development. Albert Einstein once said that “If you can’t explain it simply, you don’t understand it well enough” and I strongly believe him!

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Kumikoro

A Front End Developer's Blog

Muhammad Hassan

Full Stack developer with expertise in ASP.NET | MVC | WebAPI | Advanced Javascript | AngularJS | Angular2 | C# | ES6 | SQL | TypeScript | HTML5 | NodeJS, NUCES-FAST CS grad, MS candidate @LUMS, EX-Adjunct Faculty @NUCES-FAST, seasonal blogger & open-source contributor. Seattle, WA

Software Engineering

Web development

IEvangelist

.NET, ASP.NET, C#, MVC, TypeScript, AngularJS

leastprivilege.com

Dominick Baier on Identity & Access Control

Happy DotNetting

In Love with Technology

Knoldus

Knols of experience to your advantage

knowshnet

Search - Read - Request - Share

Rahul's space

Learn, Share and Grow with me !

Dhananjay Kumar

Developer Evangelist @Infragistics | MVP @Microsoft |

Journey to SQL Authority with Pinal Dave

SQL, SQL Server, MySQL, Big Data and NoSQL

Conficient Blog

Random bits of tech from @conficient

Code! Code! Code!

SOLID & KISS

Code Wala

Designing and coding

Microsoft Mentalist

A way to start with Microsoft Technologies

Tony Sneed's Blog

A glimpse into the lives of Tony & Zuzana Sneed

Sriramjithendra Nidumolu

Personal Notes of Sriramjithendra

%d bloggers like this: