Throwing and Catching a SOAP Fault
Server:public List<string> ListProducts()
{
...
try
{
...
}
catch (Exception e)
{
// Edit the Initial Catalog in the connection string in app.config
// to trigger this exception
if (e.InnerException is System.Data.SqlClient.SqlException)
{
throw new FaultException(
"Exception accessing database: " +
e.InnerException.Message, new FaultCode("Connect to database"));
}
else
{
throw new FaultException(
"Exception reading product numbers: " +
e.Message, new FaultCode("Iterate through products"));
}
}
// Return the list of product numbers
return productsList;
}
Client:
try
{
// Obtain a list of all products
...
// Fetch the details for a specific product
...
// Query the stock level of this product
...
// Modify the stock level of this product
...
// Disconnect from the service
...
}
catch (FaultException e)
{
Console.WriteLine("{0}: {1}", e.Code.Name, e.Reason);
}
Using Strongly Typed Faults
Throwing a FaultException is very simple but is actually not as useful as it first appears.A client application must examine the FaultException object that it catches to determine the cause of the error, soit is not easy to predict what possible exceptions could occur when invoking a WCF service. A better solution is to use strongly typed SOAP faults.
Server:
[ServiceContract]
public interface IProductsService
{
// Get the product number of every product
[FaultContract(typeof(SystemFault))]
[FaultContract(typeof(DatabaseFault))]
[OperationContract]
List<string> ListProducts();
public List<string> ListProducts()
{
...
try
{
...
}
catch (Exception e)
{
// Edit the Initial Catalog in the connection string in app.config
// to trigger this exception
if (e.InnerException is System.Data.SqlClient.SqlException)
{
DatabaseFault dbf = new DatabaseFault
{
DbOperation = "Connect to database",
DbReason = "Exception accessing database",
DbMessage = e.InnerException.Message
};
throw new FaultException<DatabaseFault>(dbf);
}
else
{
...
}
}
...
}
Client:
static void Main(string[] args)
{
...
try
{
...
}
catch (FaultException<SystemFault> sf)
{
Console.WriteLine("SystemFault {0}: {1}\n{2}",
sf.Detail.SystemOperation, sf.Detail.SystemMessage,
sf.Detail.SystemReason);
}
catch (FaultException<DatabaseFault> dbf)
{
Console.WriteLine("DatabaseFault {0}: {1}\n{2}",
dbf.Detail.DbOperation, dbf.Detail.DbMessage,
dbf.Detail.DbReason);
}
catch (FaultException e)
{
Console.WriteLine("{0}: {1}", e.Code.Name, e.Reason);
}
...
}
Reporting Unanticipated Exceptions
If a service throws a strongly-typed exception that is not specified in the service contract, the details of the exception are not propagated to the client—the exception does not form part of the WSDL description of the operation used to generate the client proxy. There will inevitably be situations where it is difficult to anticipate the exceptions that an operation could throw
In the <serviceBehaviors> section, edit the <serviceDebug> element in the <behavior>section and set the includeExceptionDetailInFaults attribute to true:
<?xml version="1.0"?>
<configuration>
...
<system.serviceModel>
...
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to
false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="false"/>
<!-- To receive exception details in faults for debugging purposes, set
the value below to true. Set to false before deployment to avoid
disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
The previous exercise used the application configuration file to specify the serviceDebug
behavior for the service. You can perform the same task by using theServiceBehavior attribute
of the class that implements the service, like this:
[ServiceBehavior(IncludeExceptionDetailInFaults=true)]
public class ProductsServiceImpl : IProductsService
{
...
}
However, it is recommended that you enable this behavior only by using the application configuration file.
Managing Exceptions in Service Host Applications
ServiceHost States
Created:
Open:The ServiceHost object moves to the Opening state while it creates the channel stacks specified by the bindings for each endpoint and starts the service.
Close:Currently running requests are allowed to complete, but clients can no longer send new requests to the service.
Abort:This method closes the service immediately without waiting for the service to finish processing client requests.
Handling Faults in a Host Application
using "ServiceHost" class
// ServiceHost object for hosting a WCF service
ServiceHost productsServiceHost;
productsServiceHost = new ServiceHost(...);
...
// Subscribe to the Faulted event of the productsServiceHost object
productsServiceHost.Faulted += (eventSender, eventArgs) =>
{
// FaultHandler method
// Runs when productsServiceHost enters the Faulted state
// Examine the properties of the productsServiceHost object
// and log the reasons for the fault
...
// Abort the service
productsServiceHost.Abort();
// Recreate the ServiceHost object
productsServiceHost = new ServiceHost(...);
// Start the service
productsServiceHost.Open();
};
Handling Unexpected Messages in a Host Application
// ServiceHost object for hosting a WCF service
ServiceHost productsServiceHost;
productsServiceHost = new ServiceHost(...);
...
// Subscribe to the UnknownMessageReceived event of the
// productsServiceHost object
productsServiceHost.UnknownMessageReceived += (eventSender, eventArgs) =>
{
// UnknownMessageReceived event handler
// Log the unknown message
...
// Display a message to the administrator
MessageBox.Show(string.Format(
"A client attempted to send the message: {0} ",
eventArgs.Message.Headers.Action));
};
...