Thursday, January 30, 2014

WCF Tutorial Part-III

WCF Tutorial Part-III
Module 5
Designing and Defining Contracts
The service contract defines how the service is perceived by users of the service. Developers who build client applications use the service contract to determine how to interact with the service, specifically which messages the service can acknowledge and how the service responds to these messages. It is important that the contract makes sense from the user's perspective and that it exposes the full capabilities of the service. Using Windows® Communication Foundation (WCF), you can define different types of interface to suit the style of your service.
·         Lesson 1:
·         Designing a Coherent and Cohesive WCF Service Interface
·        
·         To design effective service interfaces, it is essential to understand the mechanisms that underpin the exchange of messages that contracts define. This lesson examines how the contract definition affects the messages that are exchanged between client and service.

Designing by Contract

A service contract defines:
·         The operations supported by the service.
·         The parameters and return types of the operations.
·         The structure of complex data types passed.
·         Errors that may occur under normal operation.
The operations on a contract should be:
·         Coherent. You can decide what functionality is exposed by the service and at what granularity. For example, you should not just expose all methods on all classes in the service.
·         Consistent. The names, parameter types, and return types of operations should be consistent for a given contract definition.
·         Cohesive. The operations in a contract should have some form of relationship to each other, not just a mixture of random operations. If you have several distinct groups of operations, you should think about creating multiple contracts.
·         Designed for distribution. Data types and styles of interaction that work well for a local component may cause problems when exposed across a network with higher latency and bandwidth restrictions. You must design operations in a service contract with distribution in mind and employ mechanisms such as batching to decrease overheads.
The contract should be abstracted away from the implementation of the service. The service contract should avoid implementation-specific types and idioms because these may cause problems for a client that uses a different implementation technology.
In terms of services based on WCF, the contract metadata generated from the Command Language Runtime (CLR) type and exposed through Web Service Description Language (WSDL) or WS-Policy/WS-MetadataExchange is the true service contract that provides the basis for building a Service Oriented Architecture (SOA). Although you define your service contracts by using CLR types, the generated WSDL is the definitive contract.

Additional Reading

For more information about how to design service interfaces, see "Service Interfaces" on the MSDN Web site.
For more information about how the specific Microsoft® .NET Framework capabilities support aspects of contract design, see "Designing Service Contracts" on the MSDN Web site.


Contracts and Messages

Every operation on a service contract defines a message sent from client to service. The operation may also define a message passed from service to client in response to that initial message. A response message is not obligatory. An operation with no response message is called a one-way operation.
WCF uses Simple Object Access Protocol (SOAP) as its messaging format. The operation name from the service contract becomes an element in the message body when that particular operation is called. The namespace defined on the OperationContract becomes an attribute of the operation element. The combination of namespace, interface name, and operation name forms a unique address for this service which enables it to deliver messages to the correct method on the service class.

Additional Reading

For more information about the structure of SOAP messages, see "A Young Person's Guide to The Simple Object Access Protocol" on the MSDN Web site.


Contracts and Message Contents

The definition of the operation specifies the data that is passed between client and service when the operation is invoked:
·         The input parameters of the operation define the contents of the message body sent from client to service.
·         The return value and any output parameters of the operation define the contents of the message body sent as a response from service to client.
·         The error information provided for the operation defines the contents of the message body sent from service to client when an error occurs.
WCF takes responsibility for marshaling data between many built-in Microsoft .NET Framework types and the format required in the SOAP message. The default marshaling, also known as data formatting, works well for simple types but some contracts might require more complex types to pass data. You can define data contracts for complex data types, which give you more control over the serialization of your own message data.
WCF provides powerful facilities for you to take control of your own data serialization. However, you must understand that unless you do this for the express purpose of interoperability, the more control that you take, the less interoperable your services will tend to be.


Contracts and Message Handling

Some applications must control the SOAP message structure. WCF provides some control over SOAP message structure by using message contracts. Using a message contract, you can manipulate aspects of the message such as:
·         Defining custom SOAP headers
·         Adding specific elements to the SOAP body
·         Altering the namespaces in the SOAP body
WCF also enables you to work with raw messages if you define operations that pass and return a single System.ServiceModel.Channels.Message object.
These types of message handling are beyond the scope of this course.

Additional Reading

For more information about how to use message contract to control the structure of messages, see "Using Message Contracts" on the MSDN Web site.
For more information about how to use message types as parameters or return values in operations, see "Using the Message Class" on the MSDN Web site.


Contracts and Message Exchange Patterns

Contracts are based around three message exchange patterns (MEPs):
·         Request/response. In this pattern, the client sends a message and receives an associated response. This style of interaction typically looks like a synchronous remote procedure call (RPC). This is the default MEP for an operation.
·         One-way. In this pattern, the client sends a message to the service, but the service does not send a message back to the client. Individual messages can be marked as one-way.
·         Duplex. The duplex pattern consists of two separate contracts; one is implemented by the service and the other is implemented by the client. Messages can be sent by either client or service, and there is no correlation between messages sent and received. Each message sent as part of a duplex MEP is typically a one-way message. It is possible to use request-response message exchange patterns as part of a duplex contract, but this leads to threading issues that you must address very carefully.

Additional Reading

For more information about message exchange patterns, see Microsoft MSDN® Webcast: Windows Communication Foundation Top to Bottom (Part 07 of 15): Messaging Patterns (Level 200).


Contracts and Versioning

You create SOA applications by combining services that are distributed across an organization or across the Internet. At certain points, you may change your service to improve its functionality. If this change does not modify the service contract, there should be no problem. However, if the contract does change, clients that use this contract must be updated. Loose coupling precludes the forced upgrades of all clients that use the service so you must take another approach.
You can employ the same best practices for service contract versioning that you apply to component interface versioning. These best practices will usually enable existing clients to continue to work with an updated service contract. These best practices include:
·         It is okay to add operations or to add new parameters to existing operations but it is not okay to remove operations or existing parameters.
·         It is not ok to change the data types of parameters.
Problems can arise if clients validate messages or the schema of the data contained in message bodies. If this is the case, you have no option but to leave the original contract intact and to create a new contract to reflect the changed functionality. Fortunately, WCF does not validate message contents against a schema, so this is not typically a problem when you use WCF to create the client.
Similar problems will arise if you change the namespaces associated with parts of the service contract. Because of the SOAP-based nature of WCF, such namespace changes will affect the information used to locate methods associated with the operations and so will break the contract.

Additional Reading

For more information about how to version service contracts, see "Service Versioning" on the MSDN Web site.
For more information about how to version data contracts, see "Best Practices: Data Contract Versioning" on the MSDN Web site.
Lesson 2:
Defining a Service Contract
WCF provides attributes and properties that you can use to define the message exchange patterns and the contents of the messages when you design contracts. This lesson describes the ServiceContract and OperationContract attributes and their relevant properties.

Service Contracts

To indicate that a particular .NET Framework class or interface defines a service contract, apply the ServiceContract attribute. The properties of the ServiceContract attribute enable you to modify various aspects of the message exchange and message structure such as:
·         Name: Specifies the name of the service contract.
·         Namespace: Specifies the namespace associated with the service contract.
·         CallbackContract: Specifies that the service contract is part of a duplex contract pair.
·         ConfigurationName: Specifies the name used to identify this service contract in the configuration file.
·         SessionMode: Specifies how sessions are to be handled.
·         ProtectionLevel: Specifies the level of encryption and digital signing of messages required for endpoints that expose the contract.
All of these properties are discussed in more detail in the remainder of this module. The exception is SessionMode, which Module 7 "Improving WCF Service Quality" covers.

Additional Reading

For more information about the ServiceContract attribute, see "ServiceContractAttribute Class" on the MSDN Web site.


Service Names

The default name of the service contract that is exposed in the generated metadata is the name of the .NET Framework interface on which the service contract is based. However, this is not always advisable, and it is possible to give the service contract a name that is independent of the .NET Framework interface that defines the contract. For example, you may have to conform simultaneously to a legacy contract-naming scheme and different class and interface naming standards. It can be beneficial to decouple the name of the service contract from the actual interface so that it does not cause problems in future if the interface must be renamed for any reason.
You change the name used for the service contract in metadata by specifying the Name property.
[Visual Basic]
<ServiceContract(Name:="BankingService")> _
Public Interface IBank
    ...
End Interface

[Visual C#]
[ServiceContract(Name="BankingService")]
public interface IBank
{
    ...
}
The service name is also used to locate the service configuration information in the application configuration file. The following example uses the name of the service implemenation.
<services>
  <service name="ConnectedWCF.BankServiceImplementation">
  ...
</services>
Again, this may change over time, so it can be beneficial to decouple this from the class name. It also makes the name used in the configuration file shorter. You can change this name by using the ConfigurationName property. The following code and configuration example illustrate this.
[Visual Basic]
<ServiceContract(ConfigurationName:="TheBankService")> _
Public Interface IBank
    ...
End Interface

[Visual C#]
[ServiceContract(ConfigurationName=“TheBankService")]
public interface IBank
{
    ...
}

<services>
  <service name=“TheBankService">
  ...
</services>

Additional Reading

For more information about the Name and ConfigurationName properties, see "ServiceContractAttribute Class" on the MSDN Web site.

Service Namespaces

The namespace specified for the service contract is used as part of the SOAP action and is attached to operation definitions. For anything other than a simple test service, you must change the namespace from the default value of http://tempuri.org to something that uniquely identifies that the namespace belongs to your organization. This helps to distinguish between your services from those created by other organizations.
You set the namespace for the service by using the Namespace property of the ServiceContract attribute.
[Visual Basic]
<ServiceContract([Namespace]:="http://myuri.org/Simple")> _
Public Interface IBank

    <OperationContract()> _
    Function GetBalance(ByVal account As String) As Decimal

    ...

End Interface

[Visual C#]
[ServiceContract(Namespace="http://myuri.org/Simple") ]
public interface IBank
{
    [OperationContract]
    decimal GetBalance(string account);

    ...
The preceding code turns into the following SOAP elements.
<s:Header>
  <Action s:mustUnderstand="1"
xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">
http://myuri.org/Simple/IBank/GetBalance
  </Action>
</s:Header>
...
<s:Body>
  <GetBalance xmlns="http://myuri.org/Simple">
  ...
</s:Body>


Contract Implementation

When you define a service contract and service implementation class, you must follow a few simple rules.

Services implementation classes:

·         Must have a default constructor that takes no arguments. The service host invokes this constructor to instantiate the service when a client request arrives.
·         Should not have the ServiceContract attribute defined on them.
·         Can implement multiple contract interfaces.

Service contract definitions:

·         Should be separate from the implementation and be defined as a .NET Framework interface. The implementation class should implement this interface.
·         Can form part of an interface inheritance hierarchy. A service contract can inherit operations from another service contract, but the ServiceContract attribute must be reapplied at every level for its operations to be exposed correctly. You can implement all methods from all levels of inheritance hierarchy in one service class.

Demonstration: Defining a Service Contract


Lesson 3:
Defining Operations on a Service
Every service contract consists of at least one operation. When you define your operations, you can set properties that affect the way they appear to the client and the way that the client interacts with them.

Defining an Operation Contract

You apply the OperationContract attribute to a method in a service contract interface definition to expose it as an operation on the service contract. The properties of the OperationContract attribute enable you to alter various properties of the message exchange and message structure such as:
·         Name: Changes the name of the operation contract.
·         Action/ActionReply: Changes the name of the SOAP action in the request or response.
·         IsOneWay: Indicates that the operation is a one-way operation so there is no response message sent back to the client.
·         ProtectionLevel: Specifies the level of encryption and digital signing of messages required for this operation.
·         AsyncPattern: Indicates that an operation is implemented asynchronously and uses a Begin<methodName> and End<methodName> method pair in a service contract.

Additional Reading

For more information about the OperationContract attribute, see "OperationContractAttribute Class" on the MSDN Web site.


Operation Contracts and Names

The default name of the operation that is exposed in the generated metadata is the name of the .NET Framework method on which the operation is based. However, this is not always advisable, and it is possible to give the operation a name that is independent of the .NET Framework method that defines the operation. For example, you may have to conform simultaneously to a legacy operation-naming scheme and different method naming standards. It can be beneficial to decouple the name of the method from the actual operation so that it does not cause problems in future if the operation must be renamed for any reason.
You change the name used for the operation contract in metadata by using the Name property.
[Visual Basic]
<OperationContract(Name := "CustomerBalance")> _
Function GetBalance(ByVal account As String) As Decimal

[Visual C#]
[OperationContract(Name="CustomerBalance")]
decimal GetBalance(string account);
The default name of the SOAP action used in the SOAP message is a combination of the service name, the service namespace, and the operation name. If you must interoperate with an existing service then there may be specific SOAP actions that you must use. WCF enables you to set the SOAP action in the SOAP message to a specific value.
You change the name used for the SOAP action in the request by using the Action property. To change the name used for the SOAP action in the response, use the ResponseAction property.
[Visual Basic]
<OperationContract(Action:="http://myuri.org/BankBalanceRequest", _
               ReplyAction:="http://myuri.org/BankBalanceResponse")> _
    Function GetBalance(ByVal account As String) As Decimal

[Visual C#]
[OperationContract(Action=http://myuri.org/BankBalanceRequest,
                  ReplyAction="http://myuri.org/BankBalanceResponse"])
decimal GetBalance(string account);
By default, if a message arrives at a service with a SOAP action that does not match any of those supported by the service, it will be rejected. If you want to handle messages with unidentified SOAP actions, you can define an operation contract on a method with an Action property of ‘*’. This signature for this operation must take a single Message object as a parameter and return either a Message type or void.

Operation Overloading

Overloading methods is a common practice in object-oriented languages. Overloading refers to defining multiple methods with the same name that differ only by the number and type of parameters that they take. This technique works for object-oriented languages, but because services use only the method name in the action, it cannot distinguish between the two methods. The following code would cause an exception if you tried to run a service with such a contract.
[Visual Basic]
<OperationContract()> _
Sub Deposit(ByVal account As String, ByVal amount As Decimal)

<OperationContract()> _
Sub Deposit(ByVal account As String, ByVal amount As Currency)

[Visual C#]
[OperationContract]
void Deposit(string account, decimal amount);

[OperationContract]
void Deposit(string account, Currency amount);
Use the Name property to set different aliases for the two operations that become the operation names in the metadata. This will mean that they have different SOAP actions; therefore, the WCF runtime will be able to distinguish between the two different invocations.
[Visual Basic]
<OperationContract(Name:="DepositLocalCurrency")> _
Sub Deposit(ByVal account As String, ByVal amount As Decimal)

<OperationContract(Name:="DepositAnyCurrency")> _
Sub Deposit(ByVal account As String, ByVal amount As Currency)

[Visual C#]
[OperationContract(Name="DepositLocalCurrency")]
void Deposit(string account, decimal amount);

[OperationContract(Name="DepositAnyCurrency")]
void Deposit(string account, Currency amount);
The aliased names form part of the service metadata, so these names will be used as part of the client-side proxy generated by the Svcutil.exe utility. The client code can invoke them. The following code illustrates this.
[Visual Basic]
proxy.DepositLocalCurrency("ABC123", 100.00)
proxy.DepositAnyCurrency("ABC123", 555.44)

[Visual C#]
proxy.DepositLocalCurrency("ABC123", 100.00);
proxy.DepositAnyCurrency("ABC123", 555.44);


One-Way Operations

If no response is required to an operation or if the response is handled in a different way such as a message being sent to a different service, you can define the operation as one-way.
[Visual Basic]
<OperationContract(IsOneWay := true)> _
Sub MakeDeposit(ByVal account As String, ByVal amount As Decimal)

[Visual C#]
[OperationContract(IsOneWay = true)]
void MakeDeposit(string account, decimal amount);
In a one-way operation, there is no reply to the outbound message. This means that the return type of a one-way operation must be void. Another consequence of having no response message is that there is no way to detect operation failure.
Because of these characteristics, you must do things differently compared to calling request or response operations. Specifically:
  • There is uncertainty about whether the operation has been delivered. To deal with this, you can use a reliable messaging transport to guarantee delivery to the service.
  • The reply to the operation must be delivered by using a different route, channel, or queue.
One good reason to use one-way messages is the benefit to performance of the client because it does not have to wait for the service to respond or even for the message to be delivered to the service. However, the client must still wait for the message to be written to the network before it continues.

Additional Reading

For more information about one-way operations, see "How to: Create a One-Way Contract" on the MSDN Web site


Defining Duplex Contracts

A duplex contract consists of two contracts: one implemented by the service and one implemented by the client. These contracts are typically both one-way contracts to avoid thread deadlock issues. The two contracts are linked together to enable the client and the service to send messages to each other. Either the client or the service can initiate an operation, but there is no explicit correlation between client and service messages.
The callback contract is a .NET Framework interface that has one or more methods annotated by an OperationContract atrribute. However, the interface type is not annotated with the ServiceContract atrribute as the following example shows:
[Visual Basic]
Public Interface ITransferAck
    <OperationContract(IsOneWay := True)> _
    Sub TransferRecordResendRequest(ByVal transferId As Integer)
End Interface

[Visual C#]
public interface ITransferAck
{
    [OperationContract(IsOneWay = true)]
    void TransferRecordResendRequest(int transferId);
}
The service contract contains a reference to the callback contract in the CallbackContract property of the ServiceContractAttribute.
[Visual Basic]
<ServiceContract(CallbackContract := GetType(ITransferAck))> _
Public Interface IBankTransfer
    <OperationContract(IsOneWay := True)> _
    Sub ExchangeTransferRecord(ByVal transferId As Integer, _
                               ByVal record As String)
End Interface

[Visual C#]
[ServiceContract(CallbackContract=typeof(ITransferAck))]
public interface IBankTransfer
{
    [OperationContract(IsOneWay=true)]
    void ExchangeTransferRecord(int transferId, string record);
}
The service communicates with the client through a proxy reference it obtains from the OperationContext.
[Visual Basic]
Dim client As ITransferAck

client = OperationContext.Current.GetCallbackChannel(Of _
                                                     ITransferAck)()

[Visual C#]
client = OperationContext.Current.GetCallbackChannel<ITransferAck>();
Duplex contracts are often used to inform the client of events (the publish or subscribe pattern) or to poll the client from the service.

Additional Reading

For more information about duplex contracts, see "How to: Create a Duplex Contract" on the MSDN Web site.

Demonstration: One-Way Operations and Callbacks


Lesson 4:
Defining a Data Contract
WCF can marshal simple data between SOAP messages and .NET Framework types automatically. However, for more complex and user-defined types, you must guide the serialization mechanism so that it can marshal and unmarshal the required information correctly.

Data Contracts

By default, WCF handles the marshaling and unmarshaling of the following types when they are used as parameters or return types in operation contract method signatures:
·         Primitive types such as integers and strings
·         Simple composite types such as DateTime
·         XML-based types such as XmlElement
·         Collections
·         Enumerations
To pass any other complex type in a message, you must define a data contract for it.

Additional Reading

For more information about the default types supported as parameters and return values by WCF, see "Types Supported by the Data Contract Serializer" on the MSDN Web site.


Defining a Data Contract

For a complex type to be marshaled by the WCF serializer, you must define a data contract for it. To do this, start by applying the DataContractAttribute to the complex type definition that you want to pass in a message. Apply the DataMemberAttribute to each field (either a data member or a property) that is to be serialized. All other data fields on the class are ignored.
You should mark only the subset of data that you want to pass with the DataMemberAttribute. You should avoid any unnecessary properties that are difficult to serialize.
For serialization purposes, language-level visibility is irrelevant.
If you are serializing a property instead of a data member, the property must have a set accessor and a get accessor, as the following example shows:
[Visual Basic]
<DataContract()> _
Public Class TransferRecord
    <DataMember()> _
    Public targetAccount As String
   
    <DataMember()> _
    Private transferringBank As String
   
    Private internalCustomerId As String
   
    Private m_transferId As String
   
    <DataMember()> _
    Public Property TransferId() As String
        Get
            Return m_transferId
        End Get
        Set
            m_transferId = value
        End Set
    End Property
End Class

[Visual C#]
[DataContract]
public class TransferRecord
{
    [DataMember]
    public string targetAccount;

    [DataMember]
    private string transferringBank;

    private string internalCustomerId;

    private string transferId;

    [DataMember]
    public string TransferId
    {
        get { return transferId; }
        set { transferId = value; }
    }
}
Note: that in the preceding class, the value of transferId is still passed across to the other side because it is exposed by the TransferId property.

Additional Reading

For more information how to implement data contracts, see "How to: Create a Basic Data Contract for a Class or Structure" on the MSDN Web site.


Customizing a Data Contract

You can control some basic aspects of serialization by using the following properties of the DataContract attribute:
·         Name: Defines the name of the type in metadata
·         Namespace: Defines the namespace used in the schema
Other aspects of serialization are controlled by using the following properties of the DataMember attribute:
·         Name: Defines the name to use in metadata
·         IsRequired: Throws an exception if this field is not present when deserialization occurs
·         EmitDefaultValue: If this is set to true, the serializer will include the default value when serialization occurs
·         Order: Indicates at which position this member should be in the serialization sequence.

Additional Reading

For more information about DataContract attribute, see "DataContractAttribute Class" on the MSDN Web site.
For more information about DataMember attribute, see "DataMemberAttribute Class" on the MSDN Web site.


Versioning Data Contracts

The guidance for versioning data contracts is similar to the guidance for versioning service contracts:
·         Do not change the contract name or namespace.
·         Do not change data types or the order of existing data members.
·         You can add new data members if they are not marked as IsRequired.
·         If the schema of the complex data type is validated by the client, you must define a new contract.
Implementing the IExtensibleDataObject interface can ease many of the issues surrounding data contract versioning. An in-depth discussion of this interface is beyond the scope of this course, but the reference in Additional Reading provides more information.

Additional Reading

For more information about how to version data contracts, see "Forward-Compatible Data Contracts" on the MSDN Web site.


Consuming Data Contracts

The most appropriate way to consume a data contract depends on which platform or technology is used by the client and the relationship between the client and the service development.
In most cases, you will regenerate the data types consumed by the client from the service metadata. This approach keeps the client data types entirely independent of the service implementation types. However, if the same team or closely related teams develops the client and service, you can share the types between the client and the service by storing them in a common library. This approach is less flexible, but it can work well if you control both client and service. Microsoft Visual Studio® 2008 development system supports this approach by allowing you to set the "reuse proxy types" property on a service reference.


Demonstration: Defining a Data Contract


Lab: Contracts for Services and Data

Scenario

Initial user feedback for a preliminary version of the Contoso, Ltd Clinic Management System has highlighted that it takes longer than the user would like to create an appointment. An analysis of the appointment creation process has identified that a significant percentage of the time is spent waiting for the system to generate the patient letter. This must be speeded up to reach the required level of system performance.

Exercise 1: Defining and Implementing a One-Way Operation Contract


In this exercise, you will add code to make the letter-sending operation one-way and so decouple it from the client thread of execution. This will improve the response time of the system
The main tasks for this exercise are as follows:
1.    Start the 6461A-LON-DEV-05 virtual machine and log on as Student.
2.    Add a call from the ClinicAdminClient project to the Patient Letter service.
3.    Add a delay and diagnostic output to the Patient Letter service.
4.    Verify the letter creation and notification.
5.    Change the PatientLetter contract to be one-way.
6.    Verify the decoupling of client and service.

Task 1: Start the 6461A-LON-DEV-05 virtual machine and log on as Student

1.    Open the Virtual Server Remote Control Client, and then double-click 6461-LON-DEV-05.
2.    Log on to 6461A-LON-DEV-05 as Student by using the password Pa$$w0rd.

Task 2: Add a call from the ClinicAdminClient project to the Patient Letter service

1.    Start Microsoft Visual Studio 2008 as ADMINISTRATOR.
2.    Open the starter solution ConnectedWCF.sln:
    • If you are using Microsoft Visual Basic® development system, browse to the E:\Labfiles\Starter\VB\OneWay\ConnectedWCF folder, and then double-click ConnectedWCF.sln.
    • If you are using Microsoft Visual C#® development tool, browse to the E:\Labfiles\Starter\CS\OneWay\ConnectedWCF folder, and then double-click ConnectedWCF.sln..
3.    In the ClinicAdminClient project, in the MakeAppointment method,  in the ClinicAdminForm class,  locate the TODO 1 comment, and then add code to create a PatientLetterServiceContractClient proxy that uses the client endpoint name PLService_TCP. Call the SendLetter method of the proxy passing the following parameters:
    • LetterType.ConsultantionBooked
    • patientId
    • doctorId
    • slot
This will make a request/response call on the SendLetter operation on the Patient Letter service.

Task 3: Add a delay and diagnostic output to the Patient Letter service

Edit the PatientLetterServiceImplementation class in the PatientLetterService project and locate the TODO 2 comment in the SendLetter method. Edit the code in this method to output messages to the console just before the statement that calls the GetLetterTemplate method and after the statement that calls the SendTheLetter method. Both messages should display the current time.
Just before the code that outputs the second message, call the Thread.Sleep method to suspend the thread for 5 seconds. This simulates the delay seen by the end users.

Task 4: Verify the letter creation and notification

1.    Run the ConnectedWCF solution with debugging.
2.    Position the Contoso services console window and the Clinic Administration Client window so that you can see both windows.
3.    On the Clinic Administration Client form, click the Create Appointment tab, and then create the following appointment:
    • Doctor: Gastro_Jones
    • Date: Today's date
    • Slot: Click Find Slots (this selects the first slot shown)
    • Patient: NH991199Z
Verify that both of the messages from the SendLetter method appear before you see the Clinic Admin: Appointment Created window.
4.    Make a note of the appointment time, and then close the Clinic Admin: Appointment Created window.
5.    Stop debugging.
6.    In Windows Explorer, browse to the E:\Labfiles\letters\NH991199Z.txt file, open the file in Notepad, and then verify that the appointment time in the letter is the one that you selected.

Task 5: Change the PatientLetterContract to be one-way

1.    Edit the PatientLetterServiceContract class in the PatientLetterService project, locate the TODO 3 comment, and then alter the OperationContract attribute of the SendLetter operation to identify it as a one-way operation.
2.    Rebuild the Connected WCF solution.
3.    In Windows Explorer, browse to the ContosoServicesSelfHostExtConfig.exe file, and then run it as ADMINISTRATOR.
4.    In the ClinicAdminClient project, update the PatientLetterServiceReference.
5.    Close the ContosoServicesSelfHostExtConfig application.

Task 6: Verify the decoupling of client and service

1.    Run the ConnectedWCF solution with debugging.
2.    Position the Contoso services console window and the Clinic Administration Client window so that you can see both windows.
3.    On the Clinic Administration Client form, click the Create Appointment tab, and then create the following appointment:
    • Doctor: Gastro_Jones
    • Date: Today's date
    • Slot: Click Find Slots (this selects the first slot shown)
    • Patient: NH991199Z
Verify that the Clinic Admin Appointment Created window appears before at least one of the messages from the SendLetter method. This demonstrates that the call to the operation is now decoupled from the client.
4.    Make a note of the appointment time before you continue.
5.    Stop debugging.
6.    In Windows Explorer, browse to the E:\Labfiles\letters\NH991199Z.txt file, open the file in Notepad, and then verify that the appointment time in the letter is the one that you selected.
Results: After completing this exercise, you should have seen how to turn a request/response service operation into a one-way operation.

Exercise 1: Answer Key (detailed steps)


Exercise 2: Passing Complex Data with a Data Contract

Scenario

As part of the ClinicAdminClient application, you must list today's appointments including information about the patient, doctor, and time. To do this, you must retrieve a set of data for today's appointments. To keep the appointment service decoupled from its underlying technology, you have decided to pass this data as a collection of complex types that map to the Web Service Description Language (WDSL) service definition.
In this exercise, you will define a data contract on a .NET Framework class that represents an appointment. You will then pass a collection of these classes back from the Appointment service to the client.
The main tasks for this exercise are as follows:
1.    Add a data contract to the Appointment class.
2.    Use the Appointment class as a return value in the AppointmentServiceContract.
3.    Call the appointment service to list appointments.
4.    Run the client to verify the appointment listing.

Task 1: Add a data contract to the Appointment class

1.    In Microsoft Visual Studio 2008, open the starter ConnectedWCF.sln solution.
    • If you are using Visual Basic, browse to the E:\Labfiles\Starter\VB\ComplexData\ConnectedWCF folder, and then double-click ConnectedWCF.sln..
    • If you are using Visual C#, browse to the E:\Labfiles\Starter\CS\ComplexData\ConnectedWCF folder, and then double-click ConnectedWCF.sln..
2.    Edit the Appointment class in the AppointmentService project, locate the TODO 4 comment, and then add a DataContract attribute to the Appointment class with a Namespace property of http://contoso.com/ClinicManagementSystem/2007/10.
3.    Add a DataMember attribute to the Id, DoctorId, PatientId, and Time properties of the Appointment class.

Task 2: Use the Appointment class as a return value in the AppointmentServiceContract

1.    Edit the AppointmentServiceContract class in the AppointmentService project, locate the TODO 5 comment, and then add an operation named ListTodaysAppointments that takes no parameters but returns a collection of type List<Appointment> in Visual C# or List(Of Appointment) in Visual Basic.
2.    Edit the AppointmentServiceImplementation class in the AppointmentService project, locate the TODO 6 comment, and then implement the ListTodaysAppointments method by calling the ListTodaysAppointments method on an instance of the AppointmentHelper class.
3.    Rebuild the Connected WCF solution.

Task 3: Call the appointment service to list Appointments

1.    In Windows Explorer, browse to the ContosoServicesSelfHostExtConfig.exe file, and then run it as ADMINISTRATOR.
2.         In the ClinicAdminClient project, update the AppointmentServiceReference.
3.    Close the ContosoServicesSelfHostExtConfig application.
4.    Edit the ClinicAdminForm class in the ClinicAdminClient project, locate the TODO 7 comment, and then implement the RefreshAppointmentList_Click method to perform the following tasks:
    • Get an instance of the appointment service proxy.
    • Call the ListTodaysAppointments method of the proxy, assigning the return value to the DataSource property of the appointmentsToday Graphical User Interface (GUI) component.

Task 4: Run the client to verify the appointment listing

1.    Run the ConnectedWCF solution with debugging.
2.    On the Clinic Administration Client form, click the Create Appointment tab, and then create the following appointment:
    • Doctor: Gastro_Jones
    • Date: Today's date
    • Slot: Click Find Slots (this selects the first slot shown)
    • Patient: NH991199Z
3.    Click the Today's Appointments tab, and then click Refresh.
4.    Verify that you see at least one appointment listed with an ID, doctor ID, patient ID, and time. If you created appointments in the previous exercise, you should see multiple appointments.
5.    Stop debugging.
Results: After completing this exercise, you should have defined a data contract and used this to pass complex type data between a WCF service and a client.

Exercise 2: Answer Key (detailed steps)


Exercise 3: Defining and Implementing a Callback Contract

Scenario

One of the requirements of the Contoso Clinic Management System is that the clinic administrators are informed when consultants are ready to see their next patient. To achieve this, you have decided to implement a callback contract between the two client applications: the ClinicAdminClient project and the ConsultantClient project. The ClinicAdminClient project will receive a callback message when the ConsultantClient project informs the system that the consultant is ready for the next patient.
In this exercise, you will add code to enable the ClinicAdminClient application to register for callbacks with the ClinicManagementService. You will then add code to the ConsultantClient project to notify the ClinicManagementService that it is ready for the next patient. You will also add code to the ClinicManagementService to propagate any notifications to any registered ClinicManagementClients.
The main tasks for this exercise are as follows:
1.    Define a callback contract.
2.    Add the callback contract to a service contract.
3.    Enable clients to register for callbacks with the service.
4.    Implement the callback contract in the ClinicAdminClient project and register with the service.
5.    Implement the triggering method.
6.    Call the triggering method from the consultant client.
7.    Verify that callbacks are delivered to the ClinicAdminClient project.

Task 1: Define a callback contract

1.    In Microsoft Visual Studio 2008, open the starter ConnectedWCF.sln solution.
    • If you are using Visual Basic, browse to the E:\Labfiles\Starter\VB\Callback\ConnectedWCF folder, and then double-click ConnectedWCF.sln.
    • If you are using Visual C#, browse to the E:\Labfiles\Starter\CS\Callback\ConnectedWCF folder, and then double-click ConnectedWCF.sln.
2.    Edit the ClinicManagementServiceCallbackContract class in the ClinicManagementService project, locate the TODO 8 comment, and then perform the following tasks:
    • Define a service contract named ClinicManagementServiceCallbackContract and set its Namespace property to http://contoso.com/ClinicManagementSystem/2007/10.
    • Define a one-way operation for the service contract named NextPatientDueInToSeeConsultant. This operation takes a single string parameter called doctorId and has no return value.

Task 2: Add the callback contract to a service contract

Edit the ClinicManagementServiceEventsContract class in the ClinicManagementService project, locate the TODO 9 comment, and then perform the following tasks:
·         Define a service contract named ClinicManagementServiceEventsContract and set its Namespace property to http://contoso.com/ClinicManagementSystem/2007/10.
·         If you are using Visual C#, set the CallbackContract property of the ServiceContract attribute to typeof(com.contoso.ClinicManagementService.ClinicManagementServiceCallbackContract). If you are using Visual Basic, set the CallbackContract property of the ServiceContract attribute to GetType(com.contoso.ClinicManagementService.ClinicManagementServiceCallbackContract).
·         Define a one-way operation for the service contract named RegisterForEvents. This operation takes no parameters and has no return value.

Task 3: Enable clients to register for callbacks with the service

1.    Edit the ClinicManagementServiceImplementation class in the ClinicManagementService project and locate the TODO 10 comment. If you are using Visual C#, add code to create an instance variable of type List<ClinicManagementServiceCallbackContract> for storing references to the callback clients that register with the service. If you are using Visual Basic, add code to create an instance variable of type List(Of ClinicManagementServiceCallbackContract). Initialize this variable in the constructor.
2.    In the ClinicManagementServiceImplementation class, locate the TODO 11 comment, and then implement the ClinicManagementServiceEventsContract.
3.    Implement the RegisterForEvents method to obtain the callback channel for the ClinicManagementServiceCallbackContract type from the current OperationContext, and then add it to the list of callback clients that you set up earlier.
4.    Rebuild the Connected WCF solution.

Task 4: Implement the callback contract in the ClinicAdminClient project and register with the service

1.    In Windows Explorer, browse to the ContosoServicesSelfHostExtConfig.exe file, and then run it as ADMINISTRATOR.
2.    Add a service reference to the ClinicAdminClient project with the following settings:
    • Address:              net.tcp://localhost:9003/Contoso/mex
    • Services: ClinicManagementServiceImplementation
    • Namespace: ClinicManagementServiceReference
3.    Close the ContosoServicesSelfHostExtConfig application.
4.    Edit the ClinicAdminForm class in the ClinicAdminClient project, locate the TODO 12 comment, and then bring the ClinicAdminClient.ClinicManagementServiceReference namespace into scope.
5.    In the ClinicAdminForm class, locate the TODO 13 comment, and then implement the ClinicManagementServiceEventsContractCallback.
6.    Implement the generated NextPatientDueInToSeeConsultant method. This method is passed a doctorId string that you can use in a message to tell the user that the next patient for that doctor should be sent in. Add code that uses MessageBox.Show to display this message to the user.
7.    In the ClinicAdminForm constructor, locate the TODO 14 comment, and then register with the clinic management service:
    • Create an InstanceContext. If you are using Visual C#, pass this to the constructor. If you are using Visual Basic, pass Me to the constructor.
    • Create a new NetTcpBinding.
    • Create an EndpointAddress passing the location net.tcp://localhost:9003/Contoso/ClinicManagementServiceEvents
    • Create a duplex callback channel to the ClinicManagementServiceEvents endpoint of the clinic management service. Use the instance context, binding, and address as parameters to the CreateChannel method on the DuplexChannelFactory<ClinicManagementServiceEventsContract>.
    • Call the RegisterForEvents method on the duplex channel.
    • The client and service start at the same time, so you may encounter a timeout exception when you try to register. Add a loop that retries the registration up to three times if it encounters an exception; leave a 2-second gap between retries.

Task 5: Implement the triggering method

·         Edit the ClinicManagementServiceImplementation class in the ClinicManagementService project, locate the TODO 15 comment, and then implement the SendInNextPatient method. This triggers callbacks to the ClinicAdminClients in response to a message sent by the consultant client. When sending the callback messages, this method must note any unresponsive clients, consider that the connection to them is closed, and remove them from the list of callback clients. Add the following code:
·         If you are using Visual C#, declare and initialize a variable of type List<ClinicManagementServiceCallbackContract> to hold references to any unresponsive clients. If you are using Visual Basic, declare and initialize a variable of type List(Of ClinicManagementServiceCallbackContract) to hold references to any unresponsive clients.
·         Loop through the list of callback clients that you created in Task 3 and call the NextPatientDueInToSeeConsultant method on each one in turn.
·         Catch any CommunicationExceptions that occur. Add the client that caused the CommunicationException to the list of unresponsive clients.
·         Loop through the list of unresponsive clients and remove them from the list of callback clients.

Task 6: Call the triggering method from the consultant client

1.    In Windows Explorer, locate the ContosoServicesSelfHostExtConfig.exe file, and then run it as ADMINISTRATOR.
2.    In the ConsultantClient project, update the ClinicManagementServiceReference.
3.    Close the ContosoServicesSelfHostExtConfig application.
4.    Edit the ConsultantForm class in the ConsultantClient project, locate the TODO 16 comment, and then bring the ConsultantClient.ClinicManagementServiceReference namespace into scope.
5.    Locate the TODO 17 comment, and then create an instance of the ClinicManagementServiceContractClient proxy. Call the SendInNextPatient method of the proxy, passing in doctorId.Text as the parameter.

Task 7: Verify that callbacks are delivered to the ClinicAdminClient project

1.    Run the ConnectedWCF solution with debugging. This starts the Contoso services host.
2.    Start two instances of the ClinicAdminClient and one instance of the ConsultantClient.
3.    In the Consultant Client window, click the Appointment Admin tab, in the My ID list, select Gastro_Jones, and then click Next Patient.

Verify that two message boxes appear that inform you that doctor Gastro_Jones is ready for the next patient. Click OK to close each message box. This demonstrates that the call to the operation is now decoupled from the client.
4.    Close one of the Clinic Administration Client windows.
5.    In the Consultant Client window, click the Appointment Admin tab, in the My ID list, select ENT_Davies, and then click Next Patient.

Verify that you see only one message box informing you that doctor ENT_Davies is ready for the next patient. Click OK to close the message box.
6.    Stop debugging.
Results: After completing this exercise, you should have defined a callback contract and used this to communicate an event between two different service clients.

Exercise 3: Answer Key (detailed steps)

Module 6

Handling Errors

Consistent and appropriate error handling can make the difference between a system that is easy to develop, deploy, maintain, and operate and one that develops a reputation for being error-prone and unreliable. You must aim to build services that are robust and handle errors appropriately. To do this, you must be prepared to handle Microsoft® .NET Framework exceptions and turn them into something more appropriate for a distributed, cross-platform environment. In this module, you will see how to define service faults to represent error information and build these into service contracts. You will then see how to capture and process these faults in a client application.
·         Lesson 1:
·         Relating .NET Framework Exceptions to Service-Level Faults
·        
·         Simple Object Access Protocol (SOAP) provides a standard mechanism to report faults to the application that invokes a Web service. To successfully include error information in the interactions between client and service, you must map any exceptions that can occur in a service to a format that Windows® Communication Foundation (WCF) can transmit back to the client and enable the client to recognize it as a fault (remember that a client application does not have to be built by using the .NET Framework). This lesson examines how error information in a .NET Framework exception can be mapped to a SOAP fault for transmission to clients.

Relating Exceptions to Services

A large number of .NET Framework applications and components use exceptions to indicate error conditions. This poses a challenge for service-oriented applications because exceptions are an in-process error mechanism. Consider the following characteristics of .NET Framework exceptions:
·         Exceptions automatically propagate up the call chain until they encounter a boundary of some form (user interface or process boundary).
·         Exceptions contain local error information such as a stack trace that indicates where the exception occurred.
·         Exceptions contain information which is encoded in a language-specific or platform-specific structure.
·         Exception will terminate its host if it is not caught.
Errors caught or caused by a Web service are fundamentally different from .NET Framework exceptions. Specifically, they are different in the following ways:
·         Not all errors should cross the service boundary from the service to the client, even if this is the default behavior.
·         Error information from inside the service may be useless to a remote client, or it may contain security-sensitive information that should not leave the server.
·         Language-specific or platform-specific structures may be meaningless on the client side because services provide the capability for cross-platform operation.
·         One error in one operation among many hundreds or thousands of operations should not terminate the service host.


Converting Exceptions to Faults

Specifying the errors that can occur in a service is an important part of the design-by-contract approach to defining service operations. If the client is not aware of potential error conditions generated by the service, how can it handle them?
In WCF services, errors are represented in the service contract by fault contracts. Any potential errors that may occur in the service, either in the domain or business logic or due to technical reasons, should be mapped to one of the defined faults that a service operation can generate. You should define some form of generic fault to use when unexpected errors occur.
If an error occurs at run time, you must remove platform-specific information or generate more meaningful error information. You can then marshal the error information into a fault. This fault is propagated as a SOAP fault, which provides a platform-independent representation of an error back to the client.


Structure of a SOAP Fault

A SOAP fault is an XML representation of error information generated when a SOAP call fails. A SOAP fault contains several elements of interest that you can use when you define your strategy for the transmission of error information:
·         Code. This is an application-specific fault code or one of the codes defined in the SOAP specification. This is a mandatory element.
·         Reason. This is a textual description of what went wrong. This is a mandatory element.
·         Detail. This can contain application-specific data in XML form that you can use to pass more structured information about the error. This is an optional element.
The SOAP specification defines the structure of a SOAP fault. The following code example shows an example of a SOAP 1.2 fault.
[SOAP 1.2 Fault]
<s:Envelope xmlns:s=" http://www.w3.org/2003/05/soap-envelope "
            xmlns:a ="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand ="1">
      http://www.w3.org/2005/08/addressing/soap/fault
    </a:Action>
    <a:RelatesTo>
      urn:uuid:dd129ffe-a8ff-4a70-ad6f-ad48085e94e8
    </a:RelatesTo>
    <a:To s:mustUnderstand ="1">
      http://www.w3.org/2005/08/addressing/anonymous
    </a:To>
  </s:Header>
  <s:Body>
    <s:Fault>
      <s:Code>
        <s:Value> s:Sender </s:Value>
      </s:Code>
      <s:Reason>
        <s:Text xml:lang ="en-US"> ERROR INFO </s:Text>
      </s:Reason>
      <s:Detail>APPLICATION-SPECIFIC INFORMATION</s:Detail>
    </s:Fault>
  </s:Body>
</s:Envelope>
The structure of a SOAP fault changed significantly between SOAP 1.1 and SOAP 1.2. The following code example shows an example of a SOAP 1.1 fault, which is simpler.
[SOAP 1.1 Fault]
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <s:Fault>
      <faultcode xmlns="">s:Client</faultcode>
      <faultstring xml:lang="en-US" xmlns="">ERROR INFO</faultstring>
    </s:Fault>
  </s:Body>
</s:Envelope>
The binding will determine if the message exchange uses SOAP 1.2 faults or SOAP 1.1 faults. By default, WCF uses SOAP 1.2 faults unless the binding specifies otherwise; for example, the basicHttpBinding binding defaults to SOAP 1.1 for interoperability. The WCF infrastructure marshals the internal fault representation in the service into an appropriate form based on the information in the binding.

Additional Reading

For more information about SOAP faults, see the section about SOAP faults in the "SOAP 1.2 specification".


Faults in Metadata

After you add fault contracts to your service operations, the fault definitions form part of the service metadata from which you can generate client-side artifacts. As an example, consider the following fault contract.
[Visual Basic fault contract definition]
<OperationContract()> _
<FaultContract(GetType(BankService.NoFunds))> _
Sub Withdraw(ByVal account As String, ByVal amount As Decimal)
End Sub

[Visual C# fault contract definition]
[OperationContract]
[FaultContract(typeof(BankService.NoFunds))]
void Withdraw(string account, decimal amount);
This fault contract generates the following metadata.
<wsdl:operation name="Withdraw">
  <wsdl:input
 
  wsaw:Action="http://tempuri.org/MyContract/Withdraw“
        message="tns:MyContract_Withdraw_InputMessage"/>
  <wsdl:output
 
  wsaw:Action="http://tempuri.org/MyContract/WithdrawResponse“
        message="tns:MyContract_Withdraw_OutputMessage"/>
  <wsdl:fault
 
  wsaw:Action="http://tempuri.org/MyContract/WithdrawNoFundsFault“
    name=“NoFundsFault“
    message="tns:MyContract_Withdraw_NoFundsFault_FaultMessage"/> 
</wsdl:operation>
The fault forms part of the information about the operation. When you generate a client proxy, it will include code to handle this fault and marshal it into the appropriate .NET Framework data structure, including creating .NET Framework data holders for faults that contain custom type information.

Additional Reading

For more information about fault information in metadata, see the Interface Fault Reference section of the "Web Services Description Language (WSDL) Version 2.0 Part 1: Core Language specification".

Lesson 2:
Using Faults in a Service
It is important that your service is consistent in its error handling and that it generates meaningful errors. This lesson examines the WCF features that support these objectives.

Designing Service Error Handling

You can classify errors in the following way:
·         Domain errors. These are error conditions that occur when you execute business logic such as if a bank account does not have enough funds to cover a requested withdrawal. You should be able to identify all domain errors that your system can generate.
·         Expected technical errors. These are errors that do not relate to business logic but occur because of a technical reason such as if a connection request to a database times out under conditions of heavy load. You should be able to identify the majority of such technical errors that your system can generate.
·         Unexpected technical errors. These are errors that occur because of failure to deal with unexpected data values or follow unanticipated code paths during execution (commonly known as bugs) such as when an incorrect data value causes a divide-by-zero error. By their nature, it is unlikely that you will be able to anticipate these types of technical errors.
The service should inform the client about domain errors when they occur; this information should form part of the service contract definition.
You have two choices for to determine how your service can deal with technical errors:
·         Handle them internally. For example, you can retry an attempt to open a SQL connection if the first attempt times out. However, you should not continue retrying a failing operation for a long time, so good judgment is required in this type of situation.
·         Return a “something bad has happened but it was not your fault” message to the caller. In this case, the service can potentially notify the client to try again if it is a transient error such as if someone tripped over the database server power cable.

Additional Reading

For more information about error classification and handling errors in a distributed environment, see "Patterns for the Generation, Handling and Management of Errors", <http://www.eoinwoods.info/doc/europlop_2004_errorhandling.pdf> and "More Patterns for the Generation, Handling and Management of Errors".


Deciding What Information to Include in Faults

There are multiple stakeholder groups who you must inform about any errors in a service. For each constituency, you must determine what sort of information you must provide it with and how much information it requires. Typical constituencies for error information are:
·         Server-side support and development teams. This group requires the most detailed technical information so that they can track down the problem to a specific line of code that manipulates specific data. An example of such information is a stack trace as part of an error event logged to the Windows operating system event log on the server computer hosting the WCF service.
·         Client-side support and development teams. This group requires less information, but it can still be technical. An example is information about which service and server were being called when the error occurred. This can be stored as part of an error event logged to the Windows event log on the client computer.
·         End user. This group usually requires the least amount of information with the least technical content. The message can say in general terms that an error occurred and indicate if it is worth trying again. The message can also provide a reference number that can be given to the support team if the user seeks assistance.
There is a general principle that the most detailed information is required closest to the site of the error. For example, stack trace information is relevant only on the server so log it there and pass back less information to the caller.


Typed and Untyped Faults

WCF provides you with the ability to create typed and untyped faults:
·         You use the FaultException class to create an untyped fault. You can set the elements of SOAP fault such as Code, Reason, and Detail through the FaultException constructor. If your client is written by using the .NET Framework, a matching FaultException is thrown on the client side.
·         You use the generic FaultException<T> type to create a typed fault. In this case, you define a data structure to contain the information about the error and pass an instance of this data type to the FaultException<T> constructor. If your client is written by using the .NET Framework, your defined data type is thrown as a FaultException<T> exception on the client side.
If you chose to use a typed fault, you must define a data contract for the type holding the error information. The following code example shows an example of this type of data contract.
[Visual Basic]
<DataContract([Namespace] := "http://myuri.org/Simple")> _
Public Class AccountOverdrawnFault
    Private m_accountNo As String
   
    <DataMember()> _
    Public Property AccountNo() As String
        Get
            Return m_accountNo
        End Get
        Set
            m_accountNo = value
        End Set
    End Property
    ...
End Class

[Visual C#]
[DataContract(Namespace="http://myuri.org/Simple")]
public class AccountOverdrawnFault
{   
    private string accountNo;

    [DataMember]
    public string AccountNo
    {
        get { return accountNo; }
        set { accountNo = value; }
    }
    ...
}
The level of detail in the error is up to you. Depending on the sophistication of the system, you can define error codes and detail structures to be unpacked on the client; however, sometimes just the Reason string may be sufficient.

Additional Reading

For more information about how to define faults in WCF, see "Defining and Specifying Faults" on the MSDN Web site.


Using a Typed Fault

To use a typed fault, create an instance of the data type that holds the error information in the service code and pass this instance into the constructor of a generic FaultException. You then throw this populated FaultException to propagate it back to the caller. The following code example shows this.
[Visual Basic]
Private Sub Withdraw(ByVal account As String, ByVal amount As Decimal)
    Try
           
        bankBusinessObject.Withdraw(account, amount)
        ...
    Catch ex As AccountOverdrawnException
        Dim fault As _
            New AccountOverdrawnFault(ex.accountNo, _
            ex.balanceBefore, ex.amount)
        Throw New FaultException(Of AccountOverdrawnFault)(fault)
    End Try
End Sub

[Visual C#]
void Withdraw(string account, decimal amount)
{
    try
    {
        bankBusinessObject.Withdraw(account, amount);
        ...
    }
    catch (AccountOverdrawnException ex)
    {
        AccountOverdrawnFault fault =
            new AccountOverdrawnFault(ex.accountNo,
            ex.balanceBefore, ex.amount);
        throw new FaultException<AccountOverdrawnFault>(fault);
    }
}
The WCF service host handles the FaultException and converts it into a SOAP fault.

Additional Reading

For more information about how to use typed faults, see "FaultException(TDetail) Generic Class" on the MSDN Web site.


Service Behavior and Exceptions

The service host behaves differently depending on the types of exceptions that reach it. If you follow common practice, the following happens:
·         Domain errors are caught and converted into typed faults. The fault is reported back to the user.
·         Expected technical errors are caught and wrapped in untyped faults. Again, the fault is reported back to the user.
Unless it is caught in the service, an unexpected error results in an unhandled exception that propagates to the WCF service host. The WCF runtime transmits a FaultException to the client (very similar in that respect to an untyped fault) and then changes the state of the communications channel to Faulted, which causes all subsequent attempts by the client to use the channel to generate client-side exceptions. You must open a new channel by creating a new instance of the proxy. You can check the state of a channel by examining the channel’s State property to see if it is set to CommunicationState.Faulted.

Applying Consistent Error Handling

It is good practice to add an outer try block to all your service operations; the try block should encompass all code inside those operations. This practice delivers several benefits:
·         An unhandled exception cannot reach the service host, so you can prevent the channel from being faulted.
·         It provides a common, consistent location for performing exception and error handling.
Although this technique provides a level of consistency, you can end up large amounts of duplicated code in the different operations. Duplicated error-handling code leads to maintainability problems because the underlying domain code becomes harder to read. Additionally, any changes to the duplicated code must be replicated in multiple places.
To mitigate these issues, use a common error-handling mechanism that is abstracted from the domain code. WCF enables you to implement and configure an error handler that you can apply without the requirement to change the individual service operation code. To do this, use the following strategy:
1.    Define a custom error handler that implements System.ServiceModel.Dispatcher.IErrorHandler.
2.    Place your error-handling code that logs and marshals exceptions in the IErrorHandler.HandleError method.
3.    Implement the System.ServiceModel.Description.IServiceBehavior interface in your service.
4.    In the IServiceBehavior.ApplyDispatchBehavior method, add your custom error handler to the ErrorHandlers collection of the ServiceHost.ChannelDispatchers property.
You must decide between the outer try block and the custom error handler approaches. This decision is based on how consistent the error handling is across the service. If there is a lot of specific error handling for different operations, it is best to use outer try blocks.

Additional Reading

For more information about how to implement a custom error handler, see "IErrorHandler Interface" on the MSDN Web site.
For more information about why duplicated code is bad for maintainability, see Martin Fowler’s excellent book about refactoring, "Refactoring: Improving the Design of Existing Code".

Diagnosing Unexpected Exceptions

When an unexpected exception occurs, the WCF runtime generates a FaultException and sends it to the client. However, information about the cause of the exception is not passed to the client because unrestrained error information is bad for security; information about the types of exception and the data in them can provide potential hackers with clues about how your service works and possibly expose other security weaknesses. However, when you are in a trusted environment such as development or testing, you require access to information about the error so that you can find it and fix it.
To have exception information included in the service faults generated by unhandled exceptions, set the includeExceptionDetailInFaults property of the serviceDebug configuration file element to true.
<behavior name="bankServiceBehavior">
    <serviceDebug includeExceptionDetailInFaults="true"/>
    ...
</behavior>
You must remember to reset this value to false when you deploy the service to production environments.

Additional Reading

For more information about the effects of the includeExceptionDetailInFaults property, see "<serviceDebug>" on the MSDN Web site.


Demonstration: Handling and Generating Exceptions in a WCF Service


Lesson 3:
Handling Faults and Exceptions on Clients
This lesson examines how the WCF runtime propagates service faults to a client and how a client can handle these faults.

Handling Service Exceptions

A WCF client must expect various types of exception when it calls services:
·         Domain or technical typed faults that are declared as fault contracts for operations on the service contract.
·         Untyped faults that arise from unexpected exceptions inside the service which the service converts into untyped FaultExceptions.
·         Exceptions that relate to the communication between client and service. These can be errors at the transport level, or they can be caused by expectation mismatches between client and service about items such as protocol usage, security, or sessions.
Because of the relatively high probability of errors when invoking a service, you should surround all code that makes calls to a service with a try/catch block.
If the channel faults because of an unhandled exception in the service or a communication exception, you must recreate the proxy object. You can check for this by examining the channel’s State property to see if it is set to CommunicationState.Faulted.

Additional Reading

For more information about how clients can deal with WCF service faults, see Handling Faults section of "Sending and Receiving Faults" on the MSDN Web site.

Communication Exceptions

There are different types of communication-related exceptions that can occur when calling a WCF service:
·         TimeoutException. This is caused by the expiration of a communication timeout. There are different timeouts for opening a channel, closing a channel, and sending and receiving requests.

To reduce the number of timeout exceptions, you can increase the appropriate timeout on the binding configuration.
·         EndpointNotFound. This indicates the client could not locate the service endpoint.

Check that the service address that the proxy uses is a valid address and that the service is running.
·         ProtocolException. This indicates that there was a protocol negotiation problem between client and service.

The most likely cause of this exception is that the client and service use incompatible bindings.
·         CommunicationObjectFaultedException. This indicates that the channel has faulted.

To fix this error, discard the existing proxy and create a new one.
You can catch the individual exceptions and deal with them as indicated. Otherwise, you can catch the CommunicationException base class to catch all types of communication exceptions.

Handling Typed Faults

If you catch a typed fault, you can access the object that holds the error information by reading the Detail property of the FaultException<T> class. FaultException<T> is a generic class, so that the Detail property will be correctly typed.
[Visual Basic]
Try        
    bankServiceProxy.Withdraw("ABC123", 2000)
    ...
Catch ex As FaultException(Of AccountOverdrawnFault)
    Dim fault As AccountOverdrawnFault = ex.Detail
    String.Format("{0}. Account {1}, balance before {2}", _
        fault.Message, fault.Account, fault.Balance)
    Console.WriteLine(s)
End Try

[Visual C#]
try
{
    bankServiceProxy.Withdraw("ABC123", 2000.00);
    ...
}
catch (FaultException<AccountOverdrawnFault> ex)
{
    AccountOverdrawnFault fault = ex.Detail;
    String.Format("{0}. Account {1}, balance before {2}",
        fault.Message, fault.Account, fault.Balance);
    Console.WriteLine(s);
}

Additional Reading

For more information about how to catch and manipulate typed faults on clients, see "FaultException Generic Class" on the MSDN Web site.

Handling Untyped Faults

If you catch an untyped FaultException, you can access the SOAP fault information that it contains through the Reason, Detail, and Code properties. The following example illustrates this.
[Visual Basic]
Try        
    bankServiceProxy.Withdraw("ABC123", 2000)
    ...
Catch ex As FaultException
    Console.WriteLine("Service fault: {0}.", ex.Reason)
End Try

[Visual C#]
try
{
    bankServiceProxy.Withdraw("ABC123", 2000.00);
    ...
}
catch (FaultException ex)
{
    Console.WriteLine("Service fault: {0}.", ex.Reason);
}
To obtain more information about the service-side exception, set the includeExceptionDetailInFaults property in the serviceDebug element to true in the service configuration.

Additional Reading

For more information about how to handle untyped faults, see "FaultException Class" on the MSDN Web site.


Faults and Different Communication Patterns

Most discussions of WCF error handling focus on request/response contracts. Because of their synchronous nature, error handling when with this message exchange pattern has particular characteristics. If an error occurs during a request/response operation, it will be signaled before the client continues to execute. This means that the client is aware that there has been a problem and can deal with the consequences such as a faulted channel.
Things are different for one-way operations because there is no guarantee that the channel has delivered the message to the service before the client moves on. In this case, the error can go unnoticed unless other steps are taken. If this fault goes undetected by the client code, the next call it makes to any operation on that proxy will result in a CommunicationObjectFaultedException.
A duplex contract consists of two one-way contracts: the service contract and the callback contract. You can define fault contracts for the operations in a callback contract. The two one-way contracts have the same issue as described earlier, namely that an error is not indicated to calling code, so special measures have to be taken to ensure that the caller learns about the error. One advantage that duplex contracts have here is their event-like nature. It is possible to define an error notification operation on the callback contract that is called when a service-side error occurs. This ensures that the client is aware of the error as soon as it occurs and can take appropriate action. You can also do this for the service by defining an error operation as part of the service interface to be called by the client in case of error.

Additional Reading

For more information about how to weave fault handling into different types of service contract, see "Specifying and Handling Faults in Contracts and Services" on the MSDN Web site.

Demonstration: Handling Service Exceptions in a Client


Lab: Handling Errors

Scenario

As the project moves into the prototyping stage, you must define and implement a consistent error-handling policy for your services and the applications that use them. It is difficult to predict all the errors that may occur in your services, so the first thing to do is to handle any errors that the service may generate.

Exercise 1: Handle Unexpected Errors in a WCF Service

In this exercise, you will add code to catch errors in the client application and change the service behavior to pass more information about the errors to the client.
The main tasks for this exercise are as follows:
1.    Start the 6461A-LON-DEV-06 virtual machine and log on as Student.
2.    Confirm that the ClinicAdminClient project runs without errors.
3.    Force an unexpected error in the service and examine the effects.
4.    Add an outer try block to the client and report the exception to the user.
5.    Pass exception detail from service to client.

Task 1: Start the 6461A-LON-DEV-06 virtual machine and log on as Student

1.    Open the Virtual Server Remote Control Client, and then double-click 6461A-LON-DEV-06.
2.    Log on to 6461A-LON-DEV-06 as Student by using the password Pa$$w0rd.

Task 2: Confirm that the ClinicAdminClient project runs without errors

1.    Start Microsoft Visual Studio® 2008 development system as ADMINISTRATOR.
2.    Open the starter solution ConnectedWCF.sln:
    • If you are using Microsoft Visual Basic® development system, browse to the E:\Labfiles\Starter\VB\UnexpectedErrors\ConnectedWCF folder, and then double-click ConnectedWCF.sln.
    • If you are using Microsoft Visual C#® development tool, browse to the E:\Labfiles\Starter\CS\UnexpectedErrors\ConnectedWCF folder, and then double-click ConnectedWCF.sln.
3.    Run the ConnectedWCF solution with debugging.
4.    On the Clinic Administration Client form, click the Create Appointment tab, and then create the following appointment:
    • Doctor: Gastro_Jones
    • Date: Today’s date
    • Slot: Click Find Slots (this selects the first slot shown)
    • Patient: NH991199Z
5.    Click the Today's Appointments tab, and then click Refresh.

You should see the appointment that you have just added listed in this tab. You will use this listing function as the basis of the rest of the exercises.
6.    Stop debugging.

Task 3: Force an unexpected error in the service and examine the effects

1.    In the AppointmentService project, in the AppointmentHelper class, in the GetConnection method locate the TODO 1 comment and then change the connection string passed into the SqlConnection constructor as follows:
    • Data Source: .\\Dummy
    • Connection Timeout: 5
2.    Run the ConnectedWCF solution with debugging.
3.    On the Clinic Administration Client form, click the Today's Appointments tab, and then click Refresh.

After 5 seconds, you will see the Visual Studio Exception dialog box appear in the service-side code (AppointmentHelper) with the title "SQLException was unhandled by user code." This is the original exception inside the service caused by setting the invalid SQL data source value in the connection string.
4.    Continue debugging.

You will now see the Visual Studio Exception dialog box appear in the client-side code (ClinicAdminForm) with the title "FaultException was unhandled."

In the remaining tasks in this exercise, you will take steps to handle this exception in the client.
Note: If you leave it too long between the service-side exception appearing and continuing to debug, you may see a dialog box titled "TimeoutException was unhandled." In this case, restart the debugging session and perform the preceding step more rapidly.
5.    Stop debugging.

Task 4: Add an outer try block to the client and report the exception to the user

1.    Edit the ClinicAdminForm class in the ClinicAdminClient project and locate the TODO 2 comment in the RefreshAppointmentList_Click method. Add a Try/Catch block (for Visual Basic) or a try/catch block (for Visual C#) around the two WCF-related lines of code that the method contains. The catch statement should catch the Exception type and name the caught exception variable unknown.

In the catch block, call the MessageBox.Show method to display a message to the user. Pass the Show method the following parameters:
    • The Message property of the caught exception - unknown.
    • The string literal "Clinic Admin: Error".
    • MessageBoxButtons.OK.
2.    On the Debug menu, click Exceptions.
3.    In the Exceptions dialog box, clear the User-unhandled check box for Common Language Runtime Exceptions. This prevents the debug session from stopping in the service code when it encounters the unhandled SqlException.
4.    Run the ConnectedWCF solution with debugging.
5.    On the Clinic Administration Client form, click the Todays Appointments tab, and then click Refresh.

After 5 seconds, you will see the Clinic Admin: Error message box. Verify that it displays the following 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 <serviceDebug> 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."

This message obviously has nothing to do with the SQL exception—you cannot tell anything useful other than that an error has occurred. You can tell the user that an error has occurred but you cannot tell the user if it will work if they try again. From the support point of view, you cannot usefully log anything on the client that the support staff could correlate to an error on the server. You will address these issues in the remaining tasks in this lab.
6.    Stop debugging.

Task 5: Pass exception detail from service to client

1.    Edit the configuration file for the ContosoServicesSelfHostExtConfig project and add a serviceDebug element to the AppointmentServiceBehavior behavior.
2.    Under the serviceDebug node , set the value of the IncludeExceptionDetailInFaults property to true.
3.    Save the configuration file, and then run the ConnectedWCF solution with debugging.
4.    On the Clinic Administration Client form, click the Today's Appointments tab, and then click Refresh.

After 5 seconds, you will see the Clinic Admin: Error message box. Verify that it displays the following message:

"An error has occurred while establishing a connection to the server. When connecting to SQL Server 2005, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)"

This message is more informative for the development team and you can usefully log it on the client. However, it is still not too helpful for the end user. Also, if the client is deployed remotely (such as at a family practitioner’s surgery), this message contains information that might be useful to potential hackers—namely that the application is based on Microsoft SQL Server® 2005.
5.    Stop debugging.
Results: After completing this exercise, you should have seen how to handle service exceptions on the client and how to pass more detail from service to client.

Exercise 1: Answer Key (detailed steps)


Exercise 2: Adding Fault Handling to a WCF Service and the Service Contract

Scenario

When you define and implement a consistent error-handling policy for your services, you should specify which errors your services generate and under what conditions they generate these errors. These error definitions form part of the contract between the services and the applications that use them.
In this exercise, you will add code to catch errors on the client and change the service behavior to pass more information about the errors to the client.
The main tasks for this exercise are as follows:
1.    Define a fault contract.
2.    Adjust the service contract and run-time behavior so that the fault is the only mechanism for passing error information to the client.
3.    Catch and handle the exception on the server side.
4.    Adjust the client code to work with the service fault.

Task 1: Define a fault contract

1.    In Microsoft Visual Studio 2008, open the starter ConnectedWCF.sln solution:
    • If you are using Visual Basic, browse to the E:\Labfiles\Starter\VB\ServiceErrors\ConnectedWCF folder, and then double-click ConnectedWCF.sln.
    • If you are using Visual C#, browse to the E:\Labfiles\Starter\CS\ServiceErrors\ConnectedWCF folder, and then double-click ConnectedWCF.sln.
2.    Add a new class to the AppointmentService project named UnexpectedServiceFault.
3.    Make the following changes to the UnexpectedServiceFault class:
    • Delete the unnecessary statements that bring the namespaces System.Collections.Generic, System.Linq, and System.Text into scope.
    • Add a statement to bring the namespace System.Runtime.Serialization into scope.
    • Set the namespace containing the class to be com.contoso.
Note: This fault is generic across all Contoso services, so it should not be located in the AppointmentService namespace. At some point, the development team should refactor it into a separate assembly that can be shared across all Contoso services.
4.    Set the visibility of the UnexpectedServiceFault class to public and add a DataContract attribute. Set the Namespace property of the DataContract attribute to http://contoso.com/ClinicManagementSystem/2007/10.
5.    Add the following properties to the UnexpectedServiceFault class:
    • EndUserMessage. This is a public, read/write property of type string backed by a private member variable named _endUserMessage. The _endUserMessage variable should default to the value "UNDEFINED".
    • ErrorCode. This is a public, read/write property of type int backed by a private member variable named _errorCode. The _errorCode variable should default to the value -1.

      Add a DataMember attribute to both of these properties. These properties will be used to carry detail about the error to the client.

Task 2: Adjust the service contract and run-time behavior so that the fault is the only mechanism for passing error information to the client

1.    Edit the AppointmentServiceContract class in the AppointmentService project, locate the TODO 3 comment above the ListTodaysAppointments method, and then add a FaultContract for the fault type com.contoso.UnexpectedServiceFault.
2.    Turn off the functionality that causes WCF to pass exception detail to callers.

Edit the configuration file for the ContosoServicesSelfHostExtConfig project to set the value of the IncludeExceptionDetailInFaults property on the AppointmentServiceBehavior behavior to false. You will find the IncludeExceptionDetailInFaults property in the serviceDebug node.

Task 3: Catch and handle the exception on the server side

1.    Edit the AppointmentServiceImplementation class in the AppointmentService project and locate the TODO 4 comment in the ListTodaysAppointments method. Surround the call to AppointmentHelper.ListTodaysAppointments with a Try/Catch block (for Visual Basic) or a try/catch block (for Visual C#) that catches any type of Exception. Name the Exception variable unexpected.
2.    In the catch block, add code that performs the following tasks:
    • Calls the helper method LogException and passes parameters of unexpected and "ListTodaysAppointments".
    • Instantiates a new UnexpectedServiceFault and assign it to a variable called fault.
    • Sets the value of the EndUserMessage property of the fault object to "There was an unexpected error on the server. You may be able to try this operation again. If it still does not work then please contact your support department".
    • Sets the value of the ErrorCode property of the fault object to 101.
    • Instantiates a new FaultException<UnexpectedServiceFault>, passing the parameters fault and "An unexpected error occurred inside the service." to the constructor. Throw this new FaultException.
3.    Examine the LogException helper method. This method logs the information about the service-side exception to the Windows event log on the computer that is hosting the service instead of passing that information back to the client.
Note: In a production-strength implementation, you should also assign a unique error number when you log it on the service side and pass that back to the client as part of the fault so that you can track and correlate it with the server-side exception. The example code does not do this for the sake of simplicity.
4.    Rebuild the solution.

Task 4: Adjust the client code to work with the service fault

1.    In Windows Explorer, browse to the ContosoServicesSelfHostExtConfig.exe file, and then run it as ADMINISTRATOR.
2.    In the ClinicAdminClient project, update the AppointmentServiceReference.
3.    Close the ContosoServicesSelfHostExtConfig application.
4.    Edit the ClinicAdminForm class in the ClinicAdminClient project and locate the TODO 2 comment in the RefreshAppointmentList_Click method. Change the Try/Catch block (for Visual Basic) or the try/catch block (for Visual C#) so that it now catches exceptions of type FaultException<UnexpectedServiceFault> in a variable named ex.

In the MessageBox.Show method, change the first parameter to ex.Detail.EndUserMessage so that it displays the message from the UnexpectedServiceFault to the user.
5.    Run the ConnectedWCF solution WITHOUT debugging.
6.    On the Clinic Administration Client form, click the Today's Appointments tab, and then click Refresh.

After 5 seconds, you will see the Clinic Admin: Error message box. This should now show the message from the service fault that you defined earlier:

"There was an unexpected error on the server. You may be able to try this operation again. If it still does not work then please contact your support department"
Results: After completing this exercise, you should have defined a fault contract and used this to pass error detail between a WCF service and a client.

Exercise 2: Answer Key (detailed steps)

1 comment: