Contracts

In WCF, all services expose contracts. The contract is a platform-neutral and standard way of describing what the service does. WCF defines four types of contracts:

Service contracts

Describe which operations the client can perform on the service. Service contracts are the subject of the next chapter, but they are used extensively in every chapter in this book.

Data contracts

Define which data types are passed to and from the service. WCF defines implicit contracts for built-in types such as int and string, but you can easily define explicit opt-in data contracts for custom types. Chapter 3 is dedicated to defining and using data contracts, and subsequent chapters make use of data contracts as required.

Fault contracts

Define which errors are raised by the service, and how the service handles and propagates errors to its clients. Chapter 6 is dedicated to defining and using fault contracts.

Message contracts

Allow the service to interact directly with messages. Message contracts can be typed or untyped and are useful in interoperability cases when another party has already dictated some explicit (typically proprietary) message format. That, however, is by no means the usual case for common WCF applications, so this book makes no use of message contracts. Unless you are required to leverage the flexibility, power, and extensibility of message contracts, you should avoid them, as they add no value but do add complexity. In many cases, the desire to use message contracts indicates a need for a custom application context, which can be addressed using custom headers (a useful alternative technique used throughout this book). For more on message headers, see Appendix B.

The Service Contract

The ServiceContractAttribute is defined as:

[AttributeUsage(AttributeTargets.Interface|AttributeTargets.Class,
                Inherited = false)]
public sealed class ServiceContractAttribute : Attribute
{
   public string Name
   {get;set;}
   public string Namespace
   {get;set;}
   //More members
}

This attribute allows you to define a service contract. You can apply the attribute on an interface or a class, as shown in Example 1-1.

Example 1-1. Defining and implementing a service contract

[ServiceContract]
interface IMyContract
{
   [OperationContract]
   string MyMethod(string text);

   //Will not be part of the contract
   string MyOtherMethod(string text);
}
class MyService : IMyContract
{
   public string MyMethod(string text)
   {
      return "Hello " + text;
   }
   public string MyOtherMethod(string text)
   {
      return "Cannot call this method over WCF";
   }
}

The ServiceContract attribute maps a CLR interface (or inferred interface, as you will see later) to a technology-neutral service contract. The ServiceContract attribute exposes a CLR interface (or a class) as a WCF contract, independently of that type's visibility. The type visibility has no bearing on WCF, because visibility is a CLR concept. Applying the ServiceContract attribute on an internal interface exposes that interface as a public service contract, ready to be consumed across the service boundary. Without the ServiceContract attribute, the interface is not visible to WCF clients, in line with the service-oriented tenet that service boundaries should be explicit. To enforce that tenet, all contracts must explicitly opt in: only interfaces (or classes) decorated with the ServiceContract attribute will be considered as WCF contracts; other types will not.

In addition, none of the members of the type will ever be part of the contract when using the ServiceContract attribute. You must explicitly indicate to WCF which methods to expose as part of the WCF contract using the OperationContractAttribute, defined as:

[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationContractAttribute : Attribute
{
   public string Name
   {get;set;}
   //More members
}

You can apply the OperationContract attribute only on methods, and not on properties, indexers, or events, which are CLR concepts. WCF only understands operations—logical functions—and the OperationContract attribute exposes a contract method as a logical operation to perform as part of the service contract. Other methods on the interface (or class) that do not have the OperationContract attribute will not be part of the contract. This enforces explicit service boundaries and maintains an explicit opt-in model for the operations themselves. In addition, a contract operation cannot use object references as parameters: only primitive types or data contracts are allowed.

Applying the ServiceContract attribute

WCF lets you apply the ServiceContract attribute on an interface or on a class. When you apply it on an interface, some class needs to implement that interface. In general, you use plain C# or VB to implement the interface, and nothing in the service class code pertains to it being a WCF service:

[ServiceContract]
interface IMyContract
{
   [OperationContract]
   string MyMethod(  );
}
class MyService : IMyContract
{
   public string MyMethod(  )
   {
      return "Hello WCF";
   }
}

You can use implicit or explicit interface implementation:

class MyService : IMyContract
{
   string IMyContract.MyMethod(  )
   {
      return "Hello WCF";
   }
}

A single class can support multiple contracts by deriving and implementing multiple interfaces decorated with the ServiceContract attribute:

[ServiceContract]
interface IMyContract
{
   [OperationContract]
   string MyMethod(  );
}
[ServiceContract]
interface IMyOtherContract
{
   [OperationContract]
   void MyOtherMethod(  );
}
class MyService : IMyContract,IMyOtherContract
{
   public string MyMethod(  )
   {...}
   public void MyOtherMethod(  )
   {...}
}

There are, however, a few implementation constraints on the service implementation class. You should avoid parameterized constructors, because WCF will only use the default constructor. Also, although the class can use internal properties, indexers, and static members, no WCF client will ever be able to access them.

WCF also lets you apply the ServiceContract attribute directly on the service class, without defining a separate contract first:

//Avoid
[ServiceContract]
class MyService
{
   [OperationContract]
   string MyMethod(  )
   {
      return "Hello WCF";
   }
}

Under the covers, WCF will infer the contract definition. You can apply the OperationContract attribute on any method of the class, be it private or public.

Warning

Avoid using the ServiceContract attribute directly on the service class. Always define a separate contract, so that you can use it in other contexts.

Names and namespaces

You can and should define a namespace for your contract. The contract namespace serves the same purpose in WCF as it does in .NET programming: to scope a type of contract and reduce the overall chance of a collision. You use the Namespace property of the ServiceContract attribute to provide a namespace:

[ServiceContract(Namespace = "MyNamespace")]
interface IMyContract
{...}

Unspecified, the contract namespace defaults to http://tempuri.org. For outward-facing services you would typically use your company's URL, and for intranet services you can use any meaningful unique name, such as MyApplication.

By default, the exposed name of the contract will be the name of the interface used. However, you can use an alias for a contract to expose a different name to the clients in the metadata, by using the Name property of the ServiceContract attribute:

[ServiceContract(Name = "IMyContract")]
interface IMyOtherContract
{...}

Similarly, the name of the publicly exposed operation defaults to the method name, but you can use the Name property of the OperationContract attribute to alias it to a different publicly exposed name:

[ServiceContract]
interface IMyContract
{
   [OperationContract(Name = "SomeOperation")]
   void MyMethod(string text);
}

You will see a use for these properties in the next chapter.

Get Programming WCF Services, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.