Monday, March 17, 2008

Exception Handling

Let’s make a small change in the application

Open the connection string configuration and type in a wrong password. This is to ensure that you deliberately introduce a failure. Compile your service, execute your service host and then your client application.

Application will have no compilation issues. As soon as it tries to fetch the product list, you will get an exception (FaultException) as mentioned below, or something similar to this

Error message
“The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs”

Though our changes were in the connection string, .Net should have re-casted it, but what actually happened is, .NET consumes the error message, converted it into a generic FaultException and there to client so that they can be handled…..but wait……we don’t need this error. We want to see, what actually caused this problem……to see the actual error, go ahead with the following changes……we will continue on the same application we created earlier

Crete a new Data Contract with the following structure
Service side changes

[DataContract]
public class ServiceFaults
{

private string _operation;

[DataMember]
public string Operation
{
get { return _operation; }
set { _operation = value; }
}

private string _reason;

[DataMember]
public string Reason
{
get { return _reason; }
set { _reason = value; }
}


private string _message;

[DataMember]
public string Message
{
get { return _message; }
set { _message = value; }
}
}

Add this class as a Fault contract for methods that will throw error. In our case we have 3 mtthods exposed, we will add to all three methods.
Note: If needed, you can create as many Data contract classes separately and have them associated with methods, there can me multiple attributes for each method

[ServiceContract]
public interface IProductService
{
[FaultContract(typeof(ServiceFaults))]
[OperationContract]
List ListProducts();

[FaultContract(typeof(ServiceFaults))]
[OperationContract]
MyProduct GetProduct(string sProdNumber);

[FaultContract(typeof(ServiceFaults))]
[OperationContract]
int GetCurrentStockLevel(string sProdNumber);

[FaultContract(typeof(ServiceFaults))]
[FaultContract(typeof(ConfigFaults))]
[FaultContract(typeof(CustomFaults))]
[OperationContract]
bool ChangeStockLevel(string sProdNumber,
int newStockLevel, string shelf, int bin);
// TODO: Add your service operations here
}

If you take a look at the method declaration for ChangeStockLevel, there are multiple FaultContracts defined. You can follow similar mechanism for your custom error handling (one on to error logs, other to event viewer, to console, configuration error, DB errors…etc)

Let’s capture the error from the DB query…that’s where we would receive this error. Remember we changed the password for DB connection in the connection string to a wrong one. Capture the exception, get the message, add custom messages, if needed and re-cast it to client applications

public List ListProducts()
{
try
{
ProductDao pDao = new ProductDao();
return pDao.ListProducts();
}
catch (Exception ex)
{
ServiceFaults sf = new ServiceFaults();
sf.Operation = "List Products";
sf.Reason = "Exception querying Db";
sf.Message = ex.Message;
throw new FaultException(sf);
}
}


Let’s move on to client application to see if we can catch the error message
As there have been changes to the service (new Data contracts added), it will be wise to regenerate the service reference. You can do that from IDE, by clicking on the Reference file in the folder Service Reference, right-click on the file select “Update Service Reference”

It’ll take a while to get updated and from then on, it’s an easy game (as though others were tough :-))

Client side changes
private void OpenTcpClient()
{
try
{
ProductServiceClient proxy =
new ProductServiceClient("NetTcpBinding_IProductService");
List lstProducts = proxy.ListProducts().ToList();
foreach (string prod in lstProducts)
{
Console.WriteLine(prod);
}
}
catch (FaultException sf)
{
string sMessage;
sMessage = sf.Detail.Operation + "\n";
sMessage += sf.Detail.Reason + "\n";
sMessage += sf.Detail.Message;
MessageBox.Show(sMessage);
}
}

You will see appropriate error message from service

In the coming days, I’ll explain you how to secure your application through encryption, authentication and authorization….until then sit tight :-)

No comments: