static void

WCF Contracts

Service Contracts

//best practice to add name and namespace
[ServiceContract(Name = "Calculator", Namespace = "http://x.com/")]
public interface IService
{
    [OperationContract]
    Response Add(int number, [MessageParameter(Name = "Number2")]int number2);
}

Data Contracts

The messages that are passing to and from the OperationContract are data contracts.

//DataContract must be on classes/enums (and is not inherited).
[DataContract(Namespace = "http://y.com/")]
public class Response
{
    //Defaults: IsRequired=false (optional), EmitDefaultValue=true (passes nil for null/0)
    //IsRequired=true is not compatible with EmitDefaultValue=false
    [DataMember(IsRequired = false)]
    public int Total { get; set; }
}

There's also an [EnumMember] and a [CollectionDataContract]

When you have polymorphic objects/collections, add KnownTypes: [KnownType(typeof(Gorilla))]. Generic types must be returned in a Type[] from a method.

Message Contracts

For detailed control of serialization (wrapping/ headers) use [MessageContract] over dataContract. See MSDN

//default wrapping: <s:Body><SpecificRequest><Description>...
//no wrapping (IsWrapped=false): <s:Body><Description>...
//renamed (WrapperName=): <s:Body><specificRequest>...
[MessageContract(WrapperName = "specificRequest")]
public class SpecificRequest
{
    //appears in the header, encrypted and signed
    [MessageHeader(ProtectionLevel =
        System.Net.Security.ProtectionLevel.EncryptAndSign)]
    public string User { get; set; }
 
    //add a specific namespace instead of the service contract
    [MessageBodyMember(Namespace = "http://mw.com/")]
    public string Description { get; set; }
}

More control

POX

You handle things on the XML level. By default, a client puts an Action on a request which maps to the OperationContract (the SOAP Action name is {method}Request and {method}Response e.g. DoWorkRequest). You can do it manually:

//specify a specific action
[OperationContract(Action = "http://calculator/add")]
Message DoWork(Message message);
 
//or use a wildcard to handle all request with or without an action
[OperationContract(Action = "*")]
Message DoEverything(Message message);

Working with System.ServiceModel.Channels.Message

public Message DoWork(Message message)
{
    if (message.IsEmpty) return NoBodyFound();
    //message.State == Created
    XmlReader body = message.GetReaderAtBodyContents();
    //message.State == Read
 
    //or (if message.State == Created - if Read, InvalidOperationException)
    var request = message.GetBody<SpecificRequest>();
 
    //to read without setting Read state
    var messageBuffer = message.CreateBufferedCopy(int.MaxValue);
    var copy = messageBuffer.CreateMessage();
 
    foreach (var headerInfo in message.Headers)
    {
        //read the header info
    }
 
    //response - use static CreateMesage (with XmlReader, a derived BodyWriter, a MessageFault or ...)
    return Message.CreateMessage(MessageVersion.Soap12, "actionName", new SpecificRequest());
}

You can send POX messages with no SOAP envelope: add MessageVersion.None to the TextMessageEncodingBindingElement.

Behavior/Contract

Some "behaviors" (optional features) can be expressed in the contract.

Transactions

TCP and WS Bindings (not basicHttpBinding) support transactions.

Contract

[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)] //or Mandatory (NB: not OneWay!)
void DoWork();

Service Implementation

//requires transaction and will commit when finished
[OperationBehavior(TransactionScopeRequired = true,
    TransactionAutoComplete = true)]
public void DoWork()

Client binding

Must be manual!

var binding = new WSHttpBinding();
binding.TransactionFlow = true;