CLR Exceptions and SOAP Faults


A WCF service is a managed application that runs by using the .NET Framework common language runtime, or CLR. One important feature of the CLR is the protection that it provides when an error occurs; the CLR can detect many system-level errors and raise an exception if it detects any of them. A managed application can endeavor to catch these exceptions and either attempt some form of recover y or at least fail in a graceful manner, reporting the reason for the exception and providing information that can help a developer to understand the cause of the exception and take steps to rectify the situation in the future.

CLR exceptions are specific to the .NET Framework. WCF is intended to build client applications and services that are interoperable with other environments. For example, a Java client application would not understand the format of a CLR exception raised by a WCF service or how to handle it. Part of the SOAP specification describes how to format and send errors in SOAP messages, by using SOAP faults. The SOAP specification includes a schema for formatting SOAP faults as XML text and encapsulating them in a SOAP message. A SOAP fault must specify an error code and a text description of the fault (called the “reason”), and it can include other optional pieces of information. Interoperable services built by using the WCF should convert .NET Framework exceptions into SOAP faults and follow the SOAP specification for reporting these faults to client applications.

More Info 

For a detailed description of the format and contents of a SOAP fault, see the World Wide Web Consortium Web site at http://www.w3.org/TR/soap12-part1/#soapfault.

Throwing and Catching a SOAP Fault

The WCF library provides the FaultException class in the System.ServiceModel namespace. If a WCF service throws a FaultException object, the WCF runtime generates a SOAP fault message that is sent back to the client application.

In the first set of exercises in this chapter, you will add code to the WCF ProductsService service that detects selected problems when accessing the AdventureWorks database and uses the FaultException class to report these issues back to the client application.

Add code to the WCF service to throw a SOAP fault

  1. Using Visual Studio 2005, open the solution file image from book ProductsServiceFault.sln located in the Microsoft Press\WCF Step By Step\Chapter 3\ProductsServiceFault folder under your \My Documents folder.

    This solution contains three projects: a copy of the ProductsService service that you created in Chapter 1, “Introducing Windows Communication Foundation,” a copy of the ProductsServiceHost application that acts as a host for this service that you created in Chapter 2, “Hosting a WCF Service,” and a copy of the ProductsClient application that connects to the service, also from Chapter 2.

  2. In the ProductsService project, open the file image from book ProductsService.cs to display the code for the service in the code view window.

  3. Locate the ListProducts method in the ProductsServiceImpl class. You should recall from Chapter 1, that this method uses the Data Access Application Block to connect to the AdventureWorks database and retrieve the product number of every product in the Production.Product table. The product numbers are stored in a list, which is returned to the client application.

  4. Modify the statement that reads the configuration information for connecting to the database and trap any exceptions that can occur when connecting to the database, like this (shown in bold):

     // Read the configuration information for connecting to the AdventureWorks database Database dbAdventureWorks; try {     dbAdventureWorks =         DatabaseFactory.CreateDatabase("AdventureWorksConnection"); } catch(Exception e) {     throw new FaultException(     "Exception reading configuration information for the AdventureWorks database: " +         e.Message, new FaultCode("CreateDatabase")); } // Retrieve the details of all products by using a DataReader  

    If an exception occurs, this code creates a new System.ServiceModel.FaultException object with the details of the exception and throws it. The operation will stop running and will instead generate a SOAP fault, which is sent back to the client. The FaultCode object identifies the fault. The constructor used in this example simply specifies a name for the fault code.

    Note 

    If you don’t create a FaultCode object, the WCF runtime will automatically generate a FaultCode object itself, with the name “Sender” and add it to the SOAP fault sent back to the client.

  5. Modify the statements that retrieve the details of products by using a DataReader object, as follows:

     … // Retrieve the details of all products by using a DataReader IDataReader productsReader; try {     string queryString = @"SELECT ProductNumber                            FROM Production.Product";     productsReader =         dbAdventureWorks.ExecuteReader(CommandType.Text, queryString); } catch (Exception e) {     throw new FaultException(         "Exception querying the AdventureWorks database: " +         e.Message, new FaultCode("ExecuteReader")); } // Create and populate a list of products  

    If an exception occurs while running the ExecuteReader method, the service generates a SOAP fault containing the corresponding message and fault code.

  6. Modify the statements that create and populate the list of products, as shown below:

     … // Create and populate a list of products List<string> productsList = new List<string>(); try {     while (productsReader.Read())     {         string productNumber = productsReader.GetString(0);         productsList.Add(productNumber);     } } catch (Exception e) {     throw new FaultException("Exception reading product numbers: " +         e.Message, new FaultCode("Read/GetString")); } //Return the list of products return productsList;

    Again, if an exception occurs in this block of code, the service generates a SOAP fault and returns it to the client.

Add code to the WCF client application to catch a SOAP fault

  1. In the ProductsClient project, open the file image from book Program.cs to display the code for the client application in the code view window.

  2. In the Main method, add a try/catch block around the code that tests the operations in the WCF service, as shown below in bold:

     … // Test the operations in the service 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     proxy.Close(); } catch (FaultException e) {     Console.WriteLine("{0}: {1}", e.Code.Name, e.Reason); } Console.WriteLine("Press ENTER to finish"); Console.ReadLine();  

    If any of the operations generate a SOAP fault, the WCF runtime on the client creates a FaultException object. The catch handler for the FaultException object displays the fault code and reason.

Test the FaultException handler

  1. In the ProductsServiceHost project, edit the image from book App.config file. The <connectionStrings> section of this file contains the information used by the DAAB to connect to the AdventureWorks database.

  2. In the <add> element of the <connectionStrings> section, change the connectionString attribute to refer to the Junk database, as follows (do not change any other parts of the connectString attribute):

     <connectionStrings>     <add name=… connectionString="Database=Junk;" providerName= /> </connectionStrings>

  3. Build and run the solution.

    The ProductsServiceHost form and the ProductsClient console should both start.

  4. In the ProductsServiceHost form, click Start.

    If a Windows Security Alert message box appears, click Unblock. (The ProductsServiceHost application uses TCP port 8000 as the address for the WCF service.)

  5. When the service status in the ProductsServiceHost form displays “Service Running,” press Enter in the ProductsClient console.

    After a short delay, the ProductsClient console reports an exception when performing test 1 (your message might vary if you are attempting to connect to the database as a different user):

    image from book

    The ProductsService service failed when attempting to fetch the product numbers from the Junk database by running the ExecuteReader method–the SOAP fault code is “ExecuteReader.”

  6. Press Enter to close the ProductsClient console.

  7. Click Stop in the ProductsServiceHost form, and then close the form.

  8. In the image from book App.config file for the ProductsServiceHost application, change the database back to AdventureWorks in the connectionString attribute.

  9. In the <add> element of the <connectionStrings> section, change the name attribute to namex, like this:

     <connectionStrings>     <add namex="AdventureWorksConnection"  /> </connectionStrings>

  10. Build and run the solution again.

  11. In the ProductsServiceHost form, click Start.

  12. When the service status in the ProductsServiceHost form displays “Service Running,” press Enter in the ProductsClient console.

    The ProductsClient console reports a different exception when performing test 1:

    image from book

    This time, the ProductsService service failed when reading the configuration file by running the CreateDatabase method–the SOAP fault code is “CreateDatabase.” The CreateDatabase method expected to find the name attribute but instead found namex, which it did not understand.

  13. Press Enter to close the ProductsClient console.

  14. Click Stop in the ProductsServiceHost form and then close the form.

    Note 

    Do not change the namex attribute back to name just yet.

Using Strongly-Typed Faults

Throwing a FaultException is very simple but is actually not as useful as it first appears. A client application has to examine the FaultException object that it catches in order to determine the cause of the error, so it is not very easy to predict what exceptions could possibly occur when invoking a WCF service. All a developer can really do is write a very generalized catch handler with very limited scope for recovering from specific exceptions. It is analogous to using the System.Exception type to throw and handle exceptions in regular .NET Framework applications. A better solution is to use strongly typed SOAP faults.

In Chapter 1, you saw that a service contract for a WCF service contains a series of operation contracts defining the methods, or operations, that the service implements. A service contract can additionally include information about any faults that might occur when executing an operation. If an operation in a WCF service detects an exception, it can generate a specific SOAP fault message that it can send back to the client application. The SOAP fault message should contain sufficient detail to enable the user, or an administrator, to understand the reason for the exception and if possible take any necessary corrective action. A client application can use the fault information in the service contract to anticipate faults and provide specific handlers that can catch and process each different fault. These are strongly typed faults.

You specify the possible faults that can occur by using FaultContract attributes in a service contract. This is what you will do in the next set of exercises.

Note 

You can only apply the FaultContract attribute to operations that return a response. You cannot use them with one-way operations. You will learn more about one-way operations in Chapter 11, “Implementing OneWay and Asynchronous Operation.”

Use the FaultContract attribute to specify the SOAP faults an operation can throw

  1. In the ProductsServiceFault solution, in the ProductsService project, open the image from book ProductsService.cs file.

  2. In the ProductsServics.cs file, add the following classes:

     // Classes for passing fault information back to client applications [DataContract] public class ConfigFault {     [DataMember]     public string ConfigOperation;     [DataMember]     public string ConfigReason;     [DataMember]     public string ConfigMessage; }   [DataContract] public class DatabaseFault {     [DataMember]     public string DbOperation;     [DataMember]     public string DbReason;     [DataMember]     public string DbMessage; }

    These classes define types that you will use for passing the details of SOAP faults as exceptions from a service back to a client. Note that, although both classes have a similar shape, you can pass almost any type of information in a SOAP fault; the key point is that the type and its members must be serializable. These two classes use the DataContract and DataMember attributes to specify how they should be serialized.

  3. Locate the IProductsService interface.

    This interface defines the service contract for the ProductsService.

  4. In the IProductsService interface, modify the definition of the ListProducts operation as follows:

     [ServiceContract] public interface IProductsService {     // Get the product number of every product     [FaultContract(typeof(ConfigFault))]     [FaultContract(typeof(DatabaseFault))]     [OperationContract]     List<string> ListProducts();      }

    The FaultContract attributes indicate that the ListProducts method can generate SOAP faults, which a client application should be prepared to handle. The type parameter to the FaultContract attribute specifies the information that the SOAP fault will contain. In this case, the ListProducts operation can generate two types of SOAP faults: one based on the ConfigFault type, and the other based on the DatabaseFault type.

Add code to the WCF service to throw strongly typed faults

  1. In the ProductsServices.cs file, locate the ListProducts method in the ProductsServiceImpl class.

  2. Replace the code in the first catch block, as follows:

     catch(Exception e) {     ConfigFault cf = new ConfigFault();     cf.ConfigOperation = "CreateDatabase";     cf.ConfigReason = "Exception reading configuration information for the AdventureWorks database.";     cf.ConfigMessage = e.Message;     throw new FaultException<ConfigFault>(cf); }

    This block creates and populates a ConfigFault object with the details of the exception. The throw statement creates a new FaultException object based on this ConfigFault object. Note that in this case, the code makes use of the generic FaultException class; the type parameter specifies a serializable type with the type-specific details of the exception. At runtime, WCF uses the information in this object to create a SOAP fault message. The FaultException constructor is overloaded, and you can optionally specify a reason message and a fault code, as well as the ConfigFault object.

  3. Replace the code in the second catch block, as follows:

     catch (Exception e) {     DatabaseFault df = new DatabaseFault();     df.DbOperation = "ExecuteReader";     df.DbReason = "Exception querying the AdventureWorks database.";     df.DbMessage = e.Message;     throw new FaultException<DatabaseFault>(df); }

    This block of code is similar to the previous catch handler, except that it creates a DatabaseFault object and throws a FaultException based on this object. The rationale behind using a different type for the exception is that the kinds of exceptions that could arise when accessing a database are fundamentally different from the exceptions that could occur when reading configuration information. Although not shown in this example, the information returned by a database access exception could be quite different from the information returned by a configuration exception.

  4. Replace the code in the third catch block, as follows:

     catch (Exception e) {     DatabaseFault df = new DatabaseFault();     df.DbOperation = "Read/GetString";     df.DbReason = "Exception reading product numbers.";     df.DbMessage = e.Message;     throw new FaultException<DatabaseFault>(df); }

    This catch handler also uses the DatabaseFault type because the exceptions that could occur in the corresponding try block are most likely to arise from the Read method (fetching the next row of data from the database) or from the GetString method (extracting the data from the current row).

  5. Build the solution.

You can now modify the client application to handle the exceptions thrown by the service. However, first you must regenerate the proxy class that the client uses to communicate with the service.

Regenerate the proxy class for the WCF client application

  1. Open a Visual Studio 2005 Command Prompt window and move to the folder \Microsoft Press\WCF Step By Step\Chapter 3\ProductsServiceFault\ProductsService\bin folder under your \My Documents folder.

  2. Run the following command:

     svcutil ProductsService.dll

    This command runs the svcutil utility to extract the definition of the ProductsService and the other types from the assembly. It generates the following files:

    • Products.xsd. This is an XML schema file that describe the structure of the ConfigFault, Databasefault, and Products types. The svcutil utility uses the information specified in the data contracts for these types to generate this file. Part of this file, displaying the ConfigFault type, is shown below:

       … <xs:complexType name="ConfigFault">   <xs:sequence>     <xs:element minOccurs="0" name="ConfigMessage" nillable="true" type= "xs:string" />     <xs:element minOccurs="0" name="ConfigOperation" nillable="true" type= "xs:string" />     <xs:element minOccurs="0" name="ConfigReason" nillable="true" type= "xs:string" />   </xs:sequence> </xs:complexType> <xs:element name="ConfigFault" nillable="true" type="tns:ConfigFault" /> <xs:complexType name="DatabaseFault">  <xs:sequence>    <xs:element minOccurs="0" name="DbMessage" nillable="true" type="xs:string" />    <xs:element minOccurs="0" name="DbOperation" nillable="true" type= "xs:string" />    <xs:element minOccurs="0" name="DbReason" nillable="true" type="xs:string" /> </xs:sequence> </xs:complexType> …

    • Tempuri.org.xsd. This is another XML schema file. This schema describes the messages that a client can send to, or receive from, the ProductsService service. You will see later (in the WSDL file for the service), that each operation in the service is defined by a pair of messages: the first message in the pair specifies the message that the client must send to invoke the operation, and the second message specifies the response sent back by the service. This file references the data contract in the Products.xsd file to obtain the description of the Products type used by response message of the GetProduct operation. Part of this file, defining the messages for the ListProducts and GetProduct operations, is shown below:

       … <xs:element name="ListProducts">   <xs:complexType>     <xs:sequence />   </xs:complexType> </xs:element> <xs:element name="ListProductsResponse">   <xs:complexType>     <xs:sequence>       <xs:element minOccurs="0" name="ListProductsResult" nillable="true" xmlns:q 1="http://schemas.microsoft.com/2003/10/Serialization/ Arrays" type="q1:ArrayOfstring" />     </xs:sequence>   </xs:complexType> </xs:element> <xs:element name="GetProduct">   <xs:complexType>     <xs:sequence>       <xs:element minOccurs="0" name="productNumber" nillable="true" type="xs:str ing" />     </xs:sequence>   </xs:complexType> </xs:element> <xs:element name="GetProductResponse">   <xs:complexType>     <xs:sequence>       <xs:element minOccurs="0" name="GetProductResult" nillable="true" xmlns:q2= "http://schemas.datacontract.org/2004/07/Products" type="q2:Product" />     </xs:sequence>   </xs:complexType> </xs:element> …

      Note 

      The name of this file and the namespace of the types in this file are dictated by the ServiceContract attribute of the interface implemented by the service. The name Tempuri.org is the default namespace. You can change it by specifying the Namespace parameter in the ServiceContract attribute, like this:

       [ServiceContract (Namespace="Adventure-Works.com")]

    • Schemas.microsoft.com.2003.10.Serialization.Arrays.xsd. This file is another XML schema that describes how to represent an array of strings in a SOAP message. The ListProducts operation references this information in the ListProductsResponse message. The value returned by the ListProducts operation is a list of product numbers. Product numbers are held as strings, and the .NET Framework generic List<> type is serialized as an array when transmitted as part of a SOAP message.

    • Schemas.microsoft.com.2003.10.Serialization.xsd. This XML schema file describes how to represent the primitive types (such as float, int, decimal, and string) in a SOAP message, as well as some other built-in types frequently used when sending SOAP messages.

    • Tempuri.org.wsdl. This file contains the WSDL description of the service, describing how the messages and data contracts are used to implement the operations that a client application can invoke. It references the XML schema files to define the data and messages that implement operations. Notice that the definition of the ListProducts operation includes the two fault messages that you defined earlier:

       … <wsdl:operation name="ListProducts">       <wsdl:input wsaw:Action="http://tempuri.org/IProductsService/ ListProducts" message="tns:IProductsService_ListProducts_InputMessage" />       <wsdl:output wsaw:Action="http://tempuri.org/IProductsService/ ListProductsResponse" message="tns:IProductsService_ListProducts_OutputMessage" / >       <wsdl:fault wsaw:Action="http://tempuri.org/IProductsService/ ListProductsConfigFaultFault" name="ConfigFaultFault" message= "tns:IProductsService_ListProducts_ConfigFaultFault_FaultMessage" />       <wsdl:fault wsaw:Action="http://tempuri.org/IProductsService/ ListProductsDatabaseFaultFault" name="DatabaseFaultFault" message= "tns:IProductsService_ListProducts_DatabaseFaultFault_FaultMessage" /> </wsdl:operation> …

  1. You can use the WSDL file and the XML schemas to generate the proxy class. In the Visual Studio 2005 Command Prompt window, run the following command:

     svcutil /namespace:*,ProductsClient.ProductsService tempuri.org.wsdl *.xsd

    This command runs the svcutil utility again, but this time it uses the information in the WSDL file and all the schema files (*.xsd) to generate a C# source file containing a class that can act as a proxy object for the service. The namespace parameter specifies the C# namespace generated for the class (the namespace shown here has been selected to be the same as that generated by Visual Studio 2005 in the exercises in Chapter 1, to minimize the changes required to the client application). The svcutil utility creates two files:

    • image from book Products.cs. This is the source code for the proxy class.

    • image from book Output.config. This is an example application configuration file that the client application could use to configure the proxy to communicate with the service. By default, the configuration file generates an endpoint definition with the basicHttpBinding binding.

      Note 

      You can also use the svcutil utility to generate a proxy directly from a Web service endpoint rather than generating the metadata from an assembly. This is what Visual Studio 2005 does when you use the Add Service Reference command in the Project menu.

  2. In Visual Studio 2005, in the ProductsClient project, delete the Service References folder and its contents.

  3. In the Project menu, click Add Existing Item, and add the file image from book Products.cs that you have just created, from the Microsoft Press\WCF Step By Step\Chapter 3\ProductsServiceFault\ProductsService\bin folder, located under your \My Documents folder.

Add code to the WCF client application to catch a strongly typed fault

  1. In the ProductsClient project, open the image from book Program.cs file to display it in the code view window.

  2. Add the following catch handlers shown in bold after the try block in the Main method (leave the existing FaultException handler in place as well):

     catch(FaultException<ConfigFault> cf) {     Console.WriteLine("ConfigFault {0}: {1}\n{2}",         cf.Detail.ConfigOperation, cf.Detail.ConfigMessage,         cf.Detail.ConfigReason); } catch (FaultException<DatabaseFault> df) {     Console.WriteLine("DatabaseFault {0}: {1}\n{2}", df.Detail.DbOperation,         df.Detail.DbMessage, df.Detail.DbReason); } catch (FaultException e) {     Console.WriteLine("{0}: {1}", e.Code.Name, e.Reason); }

    These two handlers catch the ConfigFault and DatabaseFault exceptions. Notice that the fields containing the exception information that are populated by the ProductsService (ConfigOperation, ConfigMessage, ConfigReason, DbOperation, DbMessage, and DbReason) are located in the Detail field of the exception object.

    Important 

    You must place these two exception handlers before the non-generic FaultException handler. The non-generic handler would attempt to catch these exceptions if it occurred first, and the compiler would not let you build the solution.

  3. Build and run the solution without debugging.

  4. When the ProductsServiceHost form appears, click Start to run the service.

  5. When the service has started, in the client application console window press Enter.

    The application configuration file for the service host application still contains the attribute namex rather than name in the <add> element of the <connectionStrings> section. The service throws a ConfigException, which is serialized as a SOAP fault. The client application catches this fault and displays the details.

  6. Press Enter to close the client application. Stop the service and close the ProductsServiceHost form.

  7. Edit the image from book App.config file for the ProductsServiceHost project. Set the namex attribute in the <add> element in the <connectString> section back to name, and save the file.

  8. Edit the image from book ProductsService.cs file in the ProductsService project. In the ListProducts method in the ProductsServiceImpl class, modify the statement that reads the product number from the productsReader data reader object to try and extract the information from column 1 rather than column 0, as follows:

     while (productsReader.Read()) {     string productNumber = productsReader.GetString(1);     productsList.Add(productNumber); }

  9. Build and run the solution without debugging.

  10. When the ProductsServiceHost form appears, click Start to run the service.

  11. When the service has started, in the client application console window press Enter.

    The application configuration file for the service host application is now correct, but the code that you just modified attempts to access the second column in the data reader object, when the query only returns a single column. This “mistake” causes the service to generate a SOAP fault containing a DatabaseException with details of the Read/Getstring failure.

  12. Press Enter to close the client application. Stop the service and close the ProductsServiceHost form.

  13. Edit the image from book ProductsService.cs file in the ProductsService project. In the ListProducts method in the ProductsServiceImpl class, correct the statement that reads the product number from the productsReader data reader object to extract the information from column 0, as follows:

     while (productsReader.Read()) {     string productNumber = productsReader.GetString(0);     productsList.Add(productNumber); }

  14. Build and run the solution without debugging.

  15. In the ProductsServiceHost form, start the service. Press Enter in the client application console window. Verify that the code now runs without any exceptions. Close the client console window and the ProductsServiceHost form when you have finished.

Reporting Unanticipated Exceptions

Specifying the possible exceptions that a service can throw when performing an operation is an important part of the contract for a service. If you use strongly typed exceptions, you must specify every exception that an operation can throw in the service contract. 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 these cases, you should catch the exception in the service, and if you need to send it to the client, raise an ordinary (non-generic) FaultException as you did in the first set of exercises in this chapter.

While you are developing a WCF service, it can be useful to send information about all exceptions that occur in the service, anticipated or not, to the client application for debugging purposes. You will see how you can achieve this in the next set of exercises.

Modify the WCF service to throw an unanticipated exception

  1. In the ProductsServiceFault solution, in the ProductsService project, edit the image from book ProductsService.cs file.

  2. Add the following statement as the first line of code in the ListProducts method in the IProductsImpl class:

     public List<string> ListProducts() {     int i = 0, j = 0, k = i / j;     …

    This statement will generate a DivideByZeroException. Note that the method does not trap this exception, and it is not mentioned in the service contract.

  3. Build and run the solution without debugging.

  4. In the ProductsServiceHost form, click Start. In the client application console window, press Enter to connect to the service and invoke the ListProducts operation.

    The service throws the DivideByZero exception. However, the details of the exception are not forwarded to the client application. Instead, the WCF runtime generates a very nondescript SOAP fault that is caught by the DefaultException handler in the client:

    image from book

    This lack of detail is actually a security feature. If the service provided a complete description of the exception to the client, then, depending on the information provided, a malicious user could glean potentially useful information about the structure of the service and its internal workings.

  5. Close the client console window. Stop the service, and close the ProductsServiceHost form.

In the next exercise you will configure the host server to provide detailed information about unanticipated exceptions.

Configure the WCF service to send details of exceptions

  1. In the ProductsServiceHost project, edit the image from book App.config file.

  2. In the <serviceBehaviors> section of the image from book App.config file, remove the <serviceMetadataBehavior> element from the <behavior> section and replace it with the <serviceDebug> behavior, like this:

     <behaviors>   <serviceBehaviors>     <behavior name="ProductsBehavior">       <serviceDebug includeExceptionDetailInFaults="true"/>     </behavior>   </serviceBehaviors> </behaviors>

    Setting the includeExceptionDetailInFaults attribute to true causes WCF to transmit the full details of exceptions when it generates SOAP faults for unanticipated errors.

  3. Build and run the solution with debugging.

  4. In the ProductsServiceHost form, click Start. In the client application console window, press Enter.

    The service throws the DivideByZero exception. This time, the client is sent specific information about the exception and reports it:

    image from book

  5. Close the client console window. Stop the service, and close the ProductsServiceHost form.

  6. In the ProductsService project, edit the image from book ProductsService.cs file.

  7. In the ListProducts method, remove the line of code that causes the DivideByZeroException.

  8. In the image from book App.config file for the ProductsServiceHost project, set the includeExceptionDetailInFauts attribute of the <serviceDebug> element to false.

  9. Build and run the solution without debugging.

  10. In the ProductsServiceHost form, start the service. Press Enter in the client application console window. Verify that the code runs without any exceptions. Close the client console window and the ProductsServiceHost form when you have finished.

    The previous exercise used the application configuration file to specify the serviceDebug behavior for the service. You can perform the same task by using the ServiceBehavior 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. There are a couple of good reasons for this:

  • You can turn the behavior on and off in the configuration file without rebuilding the application. You should not deploy an application to a production environment with this behavior enabled, and it is very easy to forget that you have enabled this behavior if you use the ServiceBehavior attribute in code.

  • If you enable this behavior in code, you cannot disable it by using the application configuration file. Rather more confusingly, if you disable this behavior in code, you can enable it in the application configuration file. The general rule is that if the IncludeExceptionDetailInFaults behavior is enabled either in code or in the application configuration file, it will work. It has to be disabled in both places to turn it off. Keep life simple by only specifying this behavior in one place–the application configuration file.




Microsoft Windows Communication Foundation Step by Step
Microsoft Windows Communication Foundation Step by Step (Step By Step Developer Series)
ISBN: 0735623368
EAN: 2147483647
Year: 2007
Pages: 105
Authors: John Sharp

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net