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#]
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.
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.
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
- Lesson 2: Using Faults in a Service
- Lesson 3: Handling Faults and Exceptions on
Clients
- Lab: Handling Errors
·
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">
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))]
[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"/>
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"/>
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"/>
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.
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.
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.
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.
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.
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.
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.
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:
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 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:
"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.
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.
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.
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.
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"
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.
|
Great Article
ReplyDeleteOnline WCF Training
WCF Training
Online WCF Training from India
WCF Training in Chennai
Dot Net Training in Chennai
.Net Online Training
ASP.NET Training
C-Sharp Training