As you’ve seen, a WCF contract is nothing more than an annotated type definition. On its own, an annotated type definition does nothing, because the annotations are nothing more than metadata changes. Since the attribute annotation in a contract changes the metadata of a contract definition and reflection is a way to read metadata at run time, turning a WCF contract into something meaningful demands the use of reflection. To this end, the WCF infrastructure defines several types that use reflection to read the metadata of a contract and use that metadata as a blueprint for building endpoints. These types are called descriptions. Just as there are several types of WCF contracts, there are several types of descriptions:
System.ServiceModel.Description.ContractDescription
System.ServiceModel.Description.OperationDescription
System.ServiceModel.Description.MessageDescription
A ContractDescription describes all of the operations in a service, the OperationDescription details one operation, and a MessageDescription describes information about a message used in an operation. All of these description types are related to service contracts because a service contract defines the operations in a service, the MEPs of those operations, and the messages that those operations send and receive.
The ContractDescription type wraps an OperationDescription collection and a MessageDescription collection. Each OperationDescription maps to an operation in the service contract. Each OperationDescription has at least one MessageDescription associated with it. If the OperationDescription uses the datagram MEP, that OperationDescription contains one MessageDescription. All other MEPs have two MessageDescription objects per OperationDescription object. The ContractDescription type also defines members that correspond to other parts of the ServiceContractAttribute annotation on the service contract. For example, the ServiceContractAttribute defines a Namespace instance property. The ContractDescription type’s Namespace property is set to the same value when the ContractDescription is created.
Note | The IContractBehavior collection in the ContractDescription type does not come from a service contract. |
The ContractDescription type defines a factory method named GetContract that accepts a type as an argument. The type used for this argument must be a service contract. Once the ContractDescription object is built, it provides a means to access OperationDescription and MessageDescription objects. In normal cases, user code never directly instantiates a ContractDescription object. That job is reserved for other parts of the WCF infrastructure; I show it in this section for completeness. The following example shows how to create a ContractDescription object and illustrates how to access an OperationDescription and a MessageDescription object via the ContractDescription object:
// using directives omitted for clarity // service contract referenced in the Main method [ServiceContract(Namespace = "http://contoso.com/Restaurant")] public interface IRestaurantService3 { [OperationContract] Int32? RequestReservation(RequestReservationParams resParams,Int32? someNumber); [OperationContract] void ChangeReservation(ChangeReservationNewDateTime newDateTime); [OperationContract(IsOneWay = true)] void CancelReservation(Int32? reservationId); } class App { static void Main() { ContractDescription cDescription = ContractDescription.GetContract(typeof(IRestaurantService3)); foreach(OperationDescription opDesc in cDescription.Operations) { Console.WriteLine("\nOperation Name: {0}", opDesc.Name); foreach (MessageDescription msgDesc in opDesc.Messages) { Console.WriteLine(" Message Direction: {0}", msgDesc.Direction); Console.WriteLine(" Message Action: {0}", msgDesc.Action); Console.WriteLine(" Message Type: {0}", msgDesc.MessageType != null ? msgDesc.MessageType.ToString() : "Untyped"); } } } }
When this code runs, it produces the following output (some parts of the Message Action are omitted for clarity):
Operation Name: RequestReservation Message Direction: Input Message Action: http://contoso.com/.../RequestReservation Message Type: Untyped Message Direction: Output Message Action: http://contoso.com/.../RequestReservationResponse Message Type: Untyped Operation Name: ChangeReservation Message Direction: Input Message Action: http://contoso.com/.../ChangeReservation Message Type: ChangeReservationNewDateTime Message Direction: Output Message Action: http://contoso.com/.../ChangeReservationResponse Message Type: Untyped Operation Name: CancelReservation Message Direction: Input Message Action: http://contoso.com/.../CancelReservation Message Type: Untyped
Once built, a ContractDescription object contains all the information needed to build the rest of the infrastructure needed to send and receive messages. On the sender, the ContractDescription is an integral part of the ClientRuntime, and on the receiver, the ContractDescription is an integral part of the DispatchRuntime. At a higher level, a ContractDescription is the C part of the ABCs of WCF.