Language
Edit

Using Message Contracts

Typically when building Windows Communication Foundation (WCF) applications, developers pay close attention to the data structures and serialization issues and do not need to concern themselves with the structure of the messages in which the data is carried. For these applications, creating data contracts for the parameters or return values is straightforward. (For more information, see Specifying Data Transfer in Service Contracts.)

However, sometimes complete control over the structure of a SOAP message is just as important as control over its contents. This is especially true when interoperability is important or to specifically control security issues at the level of the message or message part. In these cases, you can create a message contract that enables you to specify the structure of the precise SOAP message required.

This topic discusses how to use the various message contract attributes to create a specific message contract for your operation.

Using Message Contracts in Operations

WCF supports operations modeled on either the remote procedure call (RPC) style or the messaging style. In an RPC-style operation, you can use any serializable type, and you have access to the features that are available to local calls, such as multiple parameters and ref and out parameters. In this style, the form of serialization chosen controls the structure of the data in the underlying messages, and the WCF runtime creates the messages to support the operation. This enables developers who are not familiar with SOAP and SOAP messages to quickly and easily create and use service applications.

The following code example shows a service operation modeled on the RPC style.

[OperationContract]
public BankingTransactionResponse PostBankingTransaction(BankingTransaction bt);

Normally, a data contract is sufficient to define the schema for the messages. For instance, in the preceding example, it is sufficient for most applications if BankingTransaction and BankingTransactionResponse have data contracts to define the contents of the underlying SOAP messages. For more information about data contracts, see Using Data Contracts.

However, occasionally it is necessary to precisely control how the structure of the SOAP message transmitted over the wire. The most common scenario for this is inserting custom SOAP headers. Another common scenario is to define security properties for the message's headers and body, that is, to decide whether these elements are digitally signed and encrypted. Finally, some third-party SOAP stacks require messages be in a specific format. Messaging-style operations provide this control.

A messaging-style operation has at most one parameter and one return value where both types are message types; that is, they serialize directly into a specified SOAP message structure. This may be any type marked with the MessageContractAttribute or the Message type. The following code example shows an operation similar to the preceding RCP-style, but which uses the messaging style.

For example, if BankingTransaction and BankingTransactionResponse are both types that are message contracts, then the code in the following operations is valid.

[OperationContract]
BankingTransactionResponse Process(BankingTransaction bt);
[OperationContract]
void Store(BankingTransaction bt);
[OperationContract]
BankingTransactionResponse GetResponse();

However, the following code is invalid.

[OperationContract]
bool Validate(BankingTransaction bt);
// Invalid, the return type is not a message contract.
[OperationContract]
void Reconcile(BankingTransaction bt1, BankingTransaction bt2);
// Invalid, there is more than one parameter.

An exception is thrown for any operation that involves a message contract type and that does not follow one of the valid patterns. Of course, operations that do not involve message contract types are not subject to these restrictions.

If a type has both a message contract and a data contract, only its message contract is considered when the type is used in an operation.

Defining Message Contracts

To define a message contract for a type (that is, to define the mapping between the type and a SOAP envelope), apply the MessageContractAttribute to the type. Then apply the MessageHeaderAttribute to those members of the type you want to make into SOAP headers, and apply the MessageBodyMemberAttribute to those members you want to make into parts of the SOAP body of the message.

The following code provides an example of using a message contract.

[MessageContract]
public class BankingTransaction
{
  [MessageHeader] public Operation operation;
  [MessageHeader] public DateTime transactionDate;
  [MessageBodyMember] private Account sourceAccount;
  [MessageBodyMember] private Account targetAccount;
  [MessageBodyMember] public int amount;
}

When using this type as an operation parameter, the following SOAP envelope is generated:


  
    Deposit
    2012-02-16T16:10:00
  
  
    
      0
      
      
    
  

Notice that operation and transactionDate appear as SOAP headers and the SOAP body consists of a wrapper element BankingTransaction containing sourceAccount, targetAccount, and amount.

You can apply the MessageHeaderAttribute and MessageBodyMemberAttribute to all fields, properties, and events, regardless of whether they are public, private, protected, or internal.

The MessageContractAttribute allows you to specify the WrapperName and WrapperNamespace attributes which control the name of the wrapper element in the body of the SOAP message. By default the name of the message contract type is used for the wrapper and the namespace in which the message contract is defined http://tempuri.org/ is used as the default namespace.

Note

KnownTypeAttribute attributes are ignored in message contracts. If a KnownTypeAttribute is required, place it on the operation that is using the message contract in question.

Controlling Header and Body Part Names and Namespaces

In the SOAP representation of a message contract, each header and body part maps to an XML element that has a name and a namespace.

By default, the namespace is the same as the namespace of the service contract that the message is participating in, and the name is determined by the member name to which the MessageHeaderAttribute or the MessageBodyMemberAttribute attributes are applied.

You can change these defaults by manipulating the MessageContractMemberAttribute.Name and MessageContractMemberAttribute.Namespace (on the parent class of the MessageHeaderAttribute and MessageBodyMemberAttribute attributes).

Consider the class in the following code example.

[MessageContract]
public class BankingTransaction
{
  [MessageHeader] public Operation operation;
  [MessageHeader(Namespace="http://schemas.contoso.com/auditing/2005")] public bool IsAudited;
  [MessageBodyMember(Name="transactionData")] public BankingTransactionData theData;
}

In this example, the IsAudited header is in the namespace specified in the code, and the body part that represents the theData member is represented by an XML element with the name transactionData. The following shows the XML generated for this message contract.


  
    false
    Deposit
  
  
    
      
    
  

Controlling Whether the SOAP Body Parts Are Wrapped

By default, the SOAP body parts are serialized inside a wrapped element. For example, the following code shows the HelloGreetingMessage wrapper element generated from the name of the MessageContractAttribute type in the message contract for the HelloGreetingMessage message.

[MessageContract]
public class HelloGreetingMessage
{
  private string localGreeting;

  [MessageBodyMember(
    Name = "Salutations",
    Namespace = "http://www.examples.com"
  )]
  public string Greeting
  {
    get { return localGreeting; }
    set { localGreeting = value; }
  }
}

/*
 The following is the request message, edited for clarity.