Saturday, July 31, 2010

This article is based on the November CTP for WinFX

Messaging is an essential element of distributed systems. For components and services to communicate across process and machine boundaries, the messaging between those components and services must be compatible or at least understood by their respective runtime environments. Messaging is usually compatible in one of these three ways: if communicating platforms are compatible; if there is an adapter that understands both platforms well enough to convert messages; or, if messages conform to a standard supported by both platforms. Considering the last statement, years ago XML made it possible to describe messages with schema, and shortly afterward web services introduced a way to generate platform-neutral messaging with Simple Object Access Protocol (SOAP), with a programmatically consumable contract described in Web Services Description Language (WSDL). Since then, advanced web services standards have continued to evolve supplying XML conformance specifications for things like security, attachments and reliability through specifications like OASIS Web Services Security (WSS), WS-Trust, WS-SecureConversation, SAML, MTOM, and WS-ReliableMessaging.

Windows Communication Foundation (WCF, formerly known as Indigo) is built upon the foundation of web services messaging and related standards, while at the same time makes it possible to serialize messages in a more compact binary format, or in a more proprietary way. Still, the core message can always be represented in XML, therefore be considered compatible with any platform that understands XML, and agrees on the contract that defines said messaging between systems.

That’s where we get to the point of this particular article. It’s all about the contract. The WCF supports three types of contracts to help describe how services and their callers communicate. A data contract describes how a complex type is serialized as part of a message. A message contract describes the entire SOAP message format, including support for custom headers and individual body elements that can be described by data contracts. The service contract describes a particular service endpoint and its supported operations. Each operation implements a particular message exchange pattern (MEP) to comprise and input message (request), and output message (response), or trigger a callback (duplex). Each of these messages is still ultimately described by a schema generated from the data contracts or message contracts associated with the operation. Each of these contracts leads to the overarching service description (WSDL) that becomes the contract for overall messaging requirements for a service and its endpoints.

There are many approaches to building service contracts with WCF including different approaches to code-first and contract-first design. You get to pick your approach based on what you have to work with, and that’s where the thrust of this article is. How do you decide if code-first is for you? What approaches to contract-first exist? And, what are we still missing?

Code-First – It’s easy, therefore you love it…
The biggest problem with contract-first today is in the tooling. Code-first implies that you get to take existing types and design an API layer (service) to expose operations and types to the outside world. This is comfortable for most developers, because they don’t have to worry about the XML - they merely let the runtime generate the WSDL contract and schema definitions for them. What is comfortable is quite often also very productive, and we like productivity don’t we? The business sure does. Of course there are many instances where code-first doesn’t cut it, but none of them have to do with interoperability, really. So long as complex types are converted to schemas that don’t leverage advanced XSD features (that may not be supported by all XML processing runtimes), messages will be interoperable. The goal, ultimately, is to generate a WSDL contract that describes service operations and type serialization via schema. We can do that using data contracts, using serializable type, or using message contracts combined with either.

What? Code-first is interoperable?

Well sure! You generate schema from code, proxies consume schema and generate a representation of the schema for the caller’s platform. Of course the serialized type has to be meaningful to the caller, so a DataSet in .NET though serializable makes a horrible candidate for cross platform interoperability. Aside from this, one of the biggest interoperability issues between platforms has to do with runtime schema support. Schema is a vast standard that has much more capability than many parsers have support for. Thus, interoperability can be an issue whether you begin with schema (contract-first) or when you generate schema from objects (code-first).

Service Contracts
In the WCF, service contracts describe the operations supported by a service, the message exchange pattern they use, and the format of each message. The service contract is the driver for generating a service description – so a service must implement at least one service contract. To create a service contract you define an interface with related methods representative of a collection of service operations, and then decorate the interface with the ServiceContractAttribute to indicate it is a service contract. Methods in the interface that should be included in the service contract are decorated with the OperationContractAttribute. Listing 1 illustrates the use of these attributes.

Listing 1: Example of a service contract.

[ServiceContract(Name="EventsService", Namespace="http://www.thatindigogirl.com/2005/12/EventsService")]
public interface IEventsService
{
[OperationContract(Name = "SaveEvent", Action =
"http://www.thatindigogirl.com/2005/12/EventsService/SaveEvent", ReplyAction =
"http://www.thatindigogirl.com/2005/12/EventsService/SaveEventsResponse")]
void SaveEvent(LinkItem item);
[OperationContract(Name = "GetEvent", Action =
"http://www.thatindigogirl.com/2005/12/EventsService/GetEvent", ReplyAction =
"http://www.thatindigogirl.com/2005/12/EventsService/GetEventResponse")]
LinkItem GetEvent();
}
Using these attributes, details like the namespace, action Uri, and message exchange pattern semantics (request-reply, one way, or callback) are controlled. In Listing 1 SaveEvent() and GetEvent() operations both follow the request-reply pattern. Explicitly setting the Name, Action and ReplyAction properties for OperationContractAttribute are not necessary, but do give you the ability to control how WSDL is generated rather than accepting the defaults. Likewise defaults are generated for the contract name and namespace however, it is important to supply values for these. By default the namespace is set to “http://tempuri.org” and the contract name uses the interface name, IEventsService. Interface naming conventions for CLR types usually prefix with the letter “I” but this is not customary for WSDL contracts so I recommend controlling the contract name.

The service merely implements this contract (possibly more than one contract) as shown in Listing 2.

Listing 2: Example of a service implementation.

public class EventsService: IEventsService
{
private LinkItem m_linkItem;

public void SaveEvent(LinkItem item)
{
m_linkItem = item;
}
public LinkItem GetEvent()
{
return m_linkItem;
}
}
Ultimately it is the contract implemented by a service that feeds the service description and the WSDL document’s list of operations and messages. The payload requirements for each message are described by schema in the WSDL, so we still need to discuss how those type schemas are generated. In this example, the data type passed to and returned from each respective operation is a LinkItem type. Types marked with the DataContractAttribute or SerializableAttribute can be included in the contract, making it possible for the service description to be generated. So, the service contract creates a frame for the overall service API, but it relies on data contracts, serializable types, and message contracts to actually generate the schema for each message.

Data Contracts
The WCF way to generate an XML schema from a CLR type is with a data contract. Data contracts describe how a CLR type maps to schema with an opt-in approach. The XmlFormatter uses the information provided in a data contract to handle serialization and deserialization. This allows developers to work with familiar objects at runtime, while the WCF runtime hides the XML goo. The XmlFormatter is the successor of the XmlSerializer for WCF services - with the exception that currently, the XmlSerializer still provides more granular control over object serialization to XML. As such there will still be cases where you will still want to use the XmlSerializer, for now.

SOURCE:http://www.theserverside.net/tt/articles/showarticle.tss?id=DesignServiceContracts

No comments:

Post a Comment