In this chapter, we will cover:
Defining a one-way Contract
Making
DataContract
forward-compatibleGenerate
DataContract
from XML SchemaUsing XMLSerializer to control message serialization
Using
MessageContract
to control the SOAP MessageAdding a custom
SoapHeader
via ContractReturning custom exception data through
FaultContract
Contracts often occur in business affairs to restrict the operations between the operators that are working with each other. For distributed communication services, Contracts also play a very important role in making sure that the service consumers can co-operate with the service providers correctly. Looking around, we can see the term SOA (Service-Oriented Architecture) being widely used. Technically speaking, SOAP (Simple Object Access Protocol) can be explained as a set of components that can be invoked, and whose interface descriptions can be published and discovered. From an SOA perspective, with Contracts properly defined, service consumers can get an idea of how to work with the target service without knowing how the service is actually implemented.
As a unified communication programming platform, WCF provides complete support for Contract-related design in various parts of WCF service development. These include ServiceContract, OperationContract, DataContract, MessageContract, FaultContract, and so on. ServiceContract
and OperationContract
are used to represent a Service and its operations' definition (such as the operation collection and operation signatures). DataContract
is used to represent an agreement of the data that will be exchanged between the service client and server. If the service designer wants to take full control over the data envelope transferred between client and server, they can use MessageContract
to control the underlying service messages. WCF also provides FaultContract
for the service designer to declaratively associate custom Exception types to certain service operations, and the corresponding fault content will be returned when an error occurs.
This chapter provides seven recipes on how to work with various contracts in WCF service development. These include defining a one-way service operation that helps you get familiar with standard ServiceContract
and OperationContract
declaration. Next, we will cover how to use FaultContractAttribute to associate a custom SOAP fault data type with certain service operations that need a customized error format. With the third, fourth, and fifth recipes, we will focus on DataContract
designing topics, such as DataContract
versioning, using XMLSerializer
for the DataContract
types serialization, and the contract-first approach for DataContract
generation. The last two recipes describe how to use MessageContract
to perform low-level manipulation on the service operations message formatting, such as returning arbitrary XML data as message content and adding a custom SOAPHeader
through MessageContract
class members.
One-way (also called diagram-style) operation is a common pattern in distributed communication programming and is also one of the three supported message exchange patterns in WCF. When using the one-way message exchange pattern, a client sends a message using a fire-and-forget exchange (refer to the next screenshot). A fire-and-forget exchange is one that requires out-of-band confirmation of successful delivery. The message might be lost in transit and never reach the service. If the send operation completes successfully at the client end, it does not guarantee that the remote endpoint has received the message. In those cases where the client only wants to send information to the server side, without taking care of the execution result, we can consider defining our WCF service operation as one-way style.

Create the service interface or class type and add methods into it.
Mark the interface/class type with
ServiceContractAttribute
and mark the methods withOperationContractAttribute
.Set the
IsOneWay
property of theOperationContractAttribute
totrue
.The following code snippet shows the complete one-way
OperationContract
definition:[ServiceContract] interface IMyContract { [OperationContract(IsOneWay=true)]void OneWayMethod(){ // Do some work here } }
When OperationContract
is marked with IsOneWay=true
, the runtime will detect this and know that this service operation needs to be handled as one-way style. One-way operation cannot carry a return value but can only pass input parameters to the service. After the client sends out the service request, the client will wait until it gets the response that the request message has reached the service side. However, the response here is not the return value, but the protocol level ACK, which indicates that the request has reached the service (but gives no idea of whether or how the request has been processed).
We can get further understanding on one-way operation via the following question:
What is the difference between a standard void (no return value) operation and a one-way operation?
Suppose you have the following ServiceContract
implemented:
[ServiceContract] public interface IHelloService { [OperationContract(IsOneWay=false)] void DoWork(); [OperationContract(IsOneWay = true)] void DoWorkAsOneWay(); }
By invoking the two operations from the client and capturing the HTTP message, we can get different response messages as shown in the next two screenshots. The first screenshot shows the response of the DoWork
operation, while the next shows the response of the DoWorkAsOneWay
operation.


As you can see, the normal void operation will return HTTP 200 status code and the complete SOAP Response in the body, while the one-way operation will only return a HTTP 202 Accepted status header. This indicates that the one-way operation call gets finished as long as the server side received the request, while the normal void operation (standard request/reply) will wait for the server side to execute and return the response data. Understanding this can help us to make better decisions about whether to use one-way operation or not.
In addition to one-way operation, there are two other message exchange patterns that are widely used in WCF services. They are the Request-response pattern and the Duplex pattern.
The Request-response pattern is very similar to the standard function call that has an input parameter and return value. In a Request-response pattern-based WCF service operation call, a message is sent and a reply is received. The pattern consists of request-response pairs, as shown in the next figure.

The Duplex exchange pattern allows an arbitrary number of messages to be sent by a client and received in any order. This pattern is like a phone conversation, where each word being spoken is a message (refer to the following screenshot).

Capture a raw http request/response of WCF service call in Chapter 12
Complete source code for this recipe can be found in the
\Chapter 1\recipe1\
folder
WCF uses a serialization engine called DataContractSerializer
by default, to serialize and deserialize data. If we want to add new complex data types (that will be transferred in service operations) in a WCF service, we need to define it as a DataContract
type so as to make it friendly to the DataContractSerializer
engine. A .NET serialization system supports backward-compatibility on custom data types naturally. However, sometimes we also need forward-compatibility for data types used in a WCF service. Suppose that you have a service that exchanges some custom data types between clients. If one side updates the custom data type (adds some fields or properties) or uses a newer version, it is important to make sure that the other side (without using the updated version of data) can still work correctly with the updated data type instances.
Make the custom data type (we will use in our service communication) implement the
IExtensibleDataObject
interface.[DataContract] public class FCQuestion : IExtensibleDataObject { [DataMember] public string Subject { get; set; } [DataMember] public string Answer { get; set; } public ExtensionDataObject ExtensionData { get; set; } }
Make sure you haven't enabled the
IgnoreExtensionDataObject
property onServiceBehaviorAttribute
applied on your WCF service (this property is disabled by default).You can have a look at the article ServiceBehaviorAttribute.IgnoreExtensionDataObject Property for more information and is available at:
After the DataContract
type implements the IExtensibleDataObject
interface, an
ExtensionDataObject
property is added; this property plays an important role in forward-compatible serialization. WCF will use DataContractSerializer
for DataContract
type serialization/deserialization. When DataContractSerializer
finds that a certain type (used for operation parameters or return value) has implemented the IExtensibleDataObject
interface, it will store any data (this is obtained from the message stream during deserialization) that doesn't have corresponding property/fields in the type definition into the ExtensionDataObject
property so that these data will not get lost. And if the deserialized instance (with some unknown data stored in ExtensionDataObject
) is serialized into the message later, DataContractSerializer
will write out ExtensionDataObject
into the message stream again. This ensures that the data in the new version of DataContract
can be consumed by the service/client with the old type definition correctly, instead of raising unexpected type, mismatching, or serialization exceptions.
The following modified data type can be consumed by the service/client that has the old definition, as explained earlier, without synchronizing the DataContract
type definition:
[DataContract] public class FCQuestion : IExtensibleDataObject { [DataMember] public string Subject { get; set; } [DataMember] public string Answer { get; set; } [DataMember] public string Comment { get; set; } public ExtensionDataObject ExtensionData { get; set; } }
Currently, using the IExtensibleDataObject
interface can make the DataContractSerializer
preserve unknown data properties/fields when deserializing/serializing custom data types. However, the ExtensionDataObject
property is an opaque object to developers and we do not have means to manually read the data stored in it. In case we want to manually extract the additional unknown property/fields, we can consider directly accessing the underlying SOAP message via MessageInspector
or other extension points.
Altering an operation message via MessageInspector in Chapter 9.
Complete source code for this recipe can be found in the
\Chapter 1\recipe2\
folder
In the contract-first development approach, one of the most important steps is to generate the data types used in the service from
XML Schemas, which represent the contract. As a unified distributed communication development platform, it is quite common to support such kind of DataContract
generation in WCF development.
If you are not yet familiar with the contract-first development approach, you can get a quick overview of it from Aaron Skonnard's MSDN article Contract-First Service Development at http://msdn.microsoft.com/en-us/magazine/cc163800.aspx.
Compose the XML schema that represents the
DataContract
types that will be used in our WCF service. The next screenshot shows a simple sample schema that contains a simple enum and a complex data type definition:Use WCF ServiceModel Metadata Utility Tool (Svcutil.exe) to generate
DataContract
type source code based on the XML Schema composed in step 1. Following is the sample command on how to use the Svcutil.exe tool:svcutil.exe /target:code /dataContractOnly /serializer:DataContractSerializer /importXmlTypesTestDataContractSchema.xsd
The generated
DataContract
is as follows:public enum LevelEnum : int { [System.Runtime.Serialization.EnumMemberAttribute()] Low = 2, ……………. } ………….. [System.Runtime.Serialization.DataContractAttribute(Name="TestData", Namespace="http://wcftest.org/datacontract")] public partial class TestData : object, System.Runtime.Serialization.IExtensibleDataObject { …………………. [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, Order=2)] public wcftest.org.datacontract.LevelEnum EnumProperty { } }
Use the generated
DataContract
in our WCF service as operation parameters or return type.
The contract-first development approach is contract/schema driven; the developers need to author the metadata/contract of the service/data. For the previous example, TestDataContractSchema.xsd
provides the contract definition of two types that will be used in our WCF service.
Svcutil.exe is a very powerful tool provided in the .NET 3.5 SDK. If you're familiar with ASP .NET ASMX Web Service development, you will find it similar to the wsdl.exe tool. You can generate a WCF client proxy and export metadata from service code. Here we just use it to generate serialization code from the given XML Schema. In the previous sample, we specify DataContractSerializer
as the serialization type (you can also use
XMLSerializer
instead, if you prefer XML serialization-oriented code).
By capturing the service operation's underlying SOAP message on wire (refer to the next screenshot), we can find that the return value's XML payload conform to the XML Schema we provided as the generation source (TestDataContractSchema.xsd
):

The DataContract
we generated here includes two typical class types—a composite type and a simple enum type. In most scenarios, people will define much more complicated data types in their services, and WCF DataContractSerializer
does provide enough support for mapping between an XML Schema-based contract and .NET code-based types. You can get more information on type mapping in the MSDN document Data Contract Schema Reference, available at:
Creating a typed service client in Chapter 4
Complete source code for this recipe can be found in the
\Chapter 1\recipe3\
folder
By default, WCF runtime uses DataContractSerializer
to perform data serialization and deserialization. However, in some cases, we will prefer using XMLSerializer
, which will give developers more control over the serialized XML content or will work more closely with some POX clients (like ASMX Web Service client).
First, we should make our data type ready for
XMLSerializer
. This can be done by adding XML serialization attributes on our data types. The followingUser
class has been decorated with several XML serialization attributes (XmlRootAttribute
for top-level type andXmlElementAttribute
for type members).[XmlRoot(ElementName="UserObject",Namespace="http://wcftest.org/xmlserializer")] public class User { [XmlElement(ElementName="FName")] public string FirstName { get; set; } [XmlElement(ElementName = "LName")] public string LastName { get; set; } [XmlElement(ElementName = "IsEnabled")] public bool Enabled { get; set; } }
Then, we need to apply
XmlSerializerFormatAttribute
on theServiceContract
type used in our service (see theITestService
interface shown as follows):[ServiceContract] [XmlSerializerFormat(Style=OperationFormatStyle.Document)] public interface ITestService { [OperationContract] void SendUser(User user); }
When we apply the XmlSerializerFormatAttribute on the ServiceContract, the WCF runtime will use XMLSerializer
as the default Serializer to serialize data and deserialize SOAP messages. Also, the auto-generated service metadata will output the data type schema based on the class's XML serialization attributes. For the User
class mentioned in the previous code example, service metadata will use the schema as shown in the next screenshot to represent its XML format:

By capturing the underlying SOAP message, we can find that the XML content of the serialized User
object conforms to the metadata schema defined earlier, which is controlled by those XML serialization attributes applied on the user
class (refer to the next screenshot):

DataContract
can help us design the data types used in a WCF service. However, this only covers the data members (variables and parameters used in operation) serialized in the underlying SOAP message. Sometimes we also need to control the structure and format of the entire SOAP message.
WCF introduces a MessageContract
concept, which helps service developers to model the structure and format of the entire message of a given service operation. Actually, we can take MessageContract
type as a special DataContract
type, which is marked by the MessageContractAttribute
. This recipe will show you how we can define a typical MessageContract
for our WCF service operation to control the format of the underlying SOAP XML message.
Define a custom data type that represents the entire SOAP message body content. Use
MessageContractAttribute
to decorate the type andMessageBodyMemberAttribute
to mark class members that will be embedded in the SOAP message body. The following code demonstrates a sampleMessageContract
pair—one is for operation request and the other for operation response.[MessageContract(WrapperName="Hello",WrapperNamespace="http:// wcftest.org/messagecontract")] public class HelloRequest { [MessageBodyMember(Name="Who")] public string User { get; set; } } [MessageContract(WrapperName="HelloResponse",WrapperNamespace="http://wcftest.org/messagecontract")] public class HelloResponse { [MessageBodyMember(Name="Reply")] public string ReplyContent { get; set; } }
After defining the
MessageContract
types for request/response operation, we need to use them as the input parameter (the only input parameter) and return value of the operation's implementation (see the followingSayHello
operation):[OperationContract] HelloResponse SayHello(HelloRequest req);
In the
SayHello
operation,HelloRequest
is the only input parameter andHelloResponse
represents the return value.
Types marked with MessageContractAttribute
can be used to represent the entire SOAP envelope body. The serialization of such types still follows the rules for normal DataContract
types.
Also, it is important that operations which use MessageContract
to control the SOAP envelope only have a single input parameter and return value. This is because only that input parameter will be serialized as the entire SOAP request body, and the return value will be serialized as the entire SOAP response body.
By capturing the SOAP request/response on wire, we can find that the serialized SOAP message content conforms to the MessageContract
definition (refer to the next two screenshots):


Creating a service via ChannelListener in Chapter 5
Complete source code for this recipe can be found in the
\Chapter 1\recipe5\
folder
The SOAP message (used by an XML Web Service and WCF service) is a standard XML document consisting of a root Envelope
tag, which in turn consists of a required Body
element and an optional Header
element. Each sub element under the optional Header
is called a SoapHeader
, which plays a similar role as the other headers, uses a certain network protocol's transmit package.
A SoapHeader is often used in SOAP messages to carry some application protocol-level data in addition to the SOAP body. WCF has used many built-in SoapHeaders for certain protocols it supports (WS-Security, WS-Reliability, and so on). For some user scenarios, we will also need to add a custom SoapHeader into the WCF service message so as to exchange additional information (mostly for communication purposes).
We need to define a custom type, which represents the SoapHeader that will be serialized in the service message. Here is a sample
DataContract
type that represents a custom header used for custom authentication:[DataContract] public class MyUsernameToken { [DataMember] public string Username { get; set; } [DataMember] public string Password { get; set; } }
Next, we can apply the custom
Header
type into our service operation'sMessageContract
. What we should do here is mark theMessageContract
member (of theHeader
type) withMessageHeaderAttribute
.[MessageContract] public class HelloRequest { [MessageHeader] public MyUsernameToken AuthUser { get; set; } [MessageBodyMember] public string User { get; set; } } [MessageContract] public class HelloResponse { [MessageBodyMember] public string Reply { get; set; } }
At the end, we need to use the
MessageContract
type as the only input parameter/return value of the particular service operation.
The MessageHeaderAttribute helps mark the particular type member (of MessageContract
type) as the SoapHeader that will be embedded in the resulting SOAP Envelope. Also, since the header is added in MessageContract
at design-time, the WCF auto-generated metadata will include the SoapHeader information, as shown in the following screenshot:

If you use Visual Studio or Svcutil.exe to generate the client proxy class, the generated proxy type will automatically map the SoapHeaders to operation parameters. Thus, when invoking the service operation, we can simply pass SoapHeader data as the operation parameter. The following code demonstrates how the auto-generated service proxy invokes the operation with the custom SoapHeader assigned.
private static void CallService() { TestProxy.TestServiceClient client = new TestProxy.TestServiceClient(); TestProxy.MyUsernameToken utoken = new TestProxy.MyUsernameToken{ Username="Foo", Password="Bar"}; string reply = client.SayHello(utoken, "WCF user"); Console.WriteLine(reply); }
By capturing the underlying SOAP message, we can find that the MyUsernameToken
header type is serialized as a SoapHeader within the <Header>
section.

In an XML Web Service and WCF service, the server side will return any unhandled exception as SoapFault
in the returned message. For WCF service operations, exceptions are returned to the client through two steps:
Map exception conditions to custom SOAP faults
Services and client send and receive SOAP faults as exceptions
By default, WCF will return the simple error message or complete exception information (including Callstack) as the Fault content. However, sometimes we want to encapsulate the raw exception information or return some user-friendly error to the client side. To support this kind of customized exception-information format, WCF has provided the FaultContract
and FaultException
features. FaultException
is a new exception type that uses Generic to help WCF service encapsulate various kinds of custom error data objects in a unified way. FaultContract
is used to formally specify the type of FaultException
that will be returned from the target WCF service operation, so that the client consumers can properly handle and parse it.
First, create the custom error type that will be returned to the service client as exception information. The custom error type is defined as a standard
DataContract
type.Then, apply the custom error type to the service operation (which will return this custom error type) through a FaultContractAttribute.
The following code shows a sample custom error type and the service operation that applies it:
[DataContract] public class UserFriendlyError { [DataMember] public string Message { get;set; } } [ServiceContract] public interface ICalcService { [OperationContract] int Divide(int lv, int rv); [OperationContract] [FaultContract(typeof(UserFriendlyError))] int DivideWithCustomException(int lv, int rv); }
Finally, we need to add our exception handling code in the service operation's implementation and return the custom error type through the
FaultException<T>
type (seeDivideWithCustomException
method shown as follows):public int Divide(int lv, int rv) { if (rv == 0) throw new Exception("Divided by Zero is not allowed!"); return lv / rv; } public int DivideWithCustomException(int lv, int rv) { if (rv == 0) throw new FaultException<UserFriendlyError>(new UserFriendlyError() { Message = "Divided by Zero is not allowed!" } ); return lv / rv; }
The FaultContractAttribute
applied on the service operation will make the runtime generate corresponding metadata entries in the WSDL document (as shown in the next screenshot). Thus, the generated client proxy knows how to handle this custom error type.

When invoking the operation, we can add code to handle the specific FaultException
and get user-friendly error information from the Exception.Detail
property.
try
{
//call the operation with invalid parameter
}
catch (FaultException<UserFriendlyError> fex)
{
Console.WriteLine("Error: {0}", fex.Detail.Message);
}
If we use Fiddler or message logging to inspect the underlying SOAP message, we can also find the custom error data in XML-serialized format:

Since the custom error type used by FaultContract
supports any valid DataContract
type, we can define various kinds of custom exception data content (from simple primitive types to complex nested classes).
Generating DataContract from XML Schema
Capturing a WCF request/response message via Fiddler tool in Chapter 12
Complete source code for this recipe can be found in the
\Chapter 1\recipe7\
folder