Contracts are another critical component of the policy that
must be agreed upon between clients and services. Contracts up until today
were written with either IDL (Interface Definition Language) or WSDL (Web Service
Description Language). WSDL is the most recent attempt at answering the what
a service is supposed to do. WSDL has the same information you see in IDL plus
much more. WSDL is not really changing with the introduction of WCF services;
what does change is how we go about defining what our WSDL should look like.
WCF services have defined a variety of contract types that
are used to put together the WSDL document that a service exposes. Service
contracts, data contracts, and message contracts all help to define the WSDL a
service will expose. I will walk us through the different contract types and
map them to those elements of WSDL we are already familiar with. This will
help to show some of the obvious parallels between WSDL and WCF service definitions.
Service Contracts
Service contracts are the API for your service. These
contracts can be defined using interfaces, or they can be defined on the actual
class that implements the service behavior. The following is an example of a
service contract using a C# interface.
IFamilyGuyGoodies.cs
[ServiceContract(Namespace
= "http://familyguyquotes")]
public interface IFamilyGuyGoodies
{
[OperationContract]
string
GetQuote(string familyGuyCharacter);
}
As you have probably already realized, this service contract
does not specify much. This really just demonstrates the use of an interface
instead of a class to define a service. You should take note that the access
modifiers have been left off intentionally. The concept of public and
private are meaningless when identifying methods on a service contract. If
you expose that functionality using the [OperationContract] then it will be
capable of being invoked externally. This again goes back to making explicit
decisions about the behavior that is exposed.
It is also important to note that within the service
contract and operation contract there are a number of declarative settings that
can be enforced. For example, if this method was intended to use simplex
messaging then you would specify IsOneWay=True. It is also within this service
contract definition that I can make explicit assertions about my binding
requirements. I would add an attribute [BindingRequirements()] to specify
transaction flow requirements or reliable messaging requirements.
The service contract maps to the portType section of the
WSDL document. With the current ASMX stack the [webMethods()] that you define
are what make up this section. With WCF, you are capable of making specific
decisions about how you want consumers to interact with your service by
building interfaces first before diving right into implementation. This
decision to focus on API first can lead to much better decisions about what is
truly required to invoke a service operation.
Data Contracts
WCF gives us a whole new level of control for how we specify
the data that is passed back and forth between the client and the service. A
data contract allows us to specify what members of a custom type are serialized
and how those members are serialized. The data contracts are analogous to the
type element in WSDL today. The following is an example of a data contract in
WCF.
Quote.cs
[DataContract]
class Quote
{
[DataMember] string quoteText;
[DataMember(VersionAdded=2)]string episodeFound;
}
I have again used a simple type to demonstrate the most
obvious advantages of the data contracts with WCF. Notice the [DataMember]
attribute; this is the only way to identify to the serializer that this member
should be expected as part of the SOAP payload. As with service contracts, the
access modifiers are meaningless.
I also used this simple example to showcase the implicit
versioning available in the data contract. Notice that the episodeFound
member above was added in a later version of the data contract. This in turn
allows a client to specify that it is using version 1 of this service and that that
portion of the data is not required to communicate successfully with the
service.
WCF data contracts give developers their first set of built-in
versioning capabilities. Abiding by some very simple rules will allow for the
flexibility to add and remove portions of your data being sent back and forth
to a service. This reinforces a couple of service tenets. First, you are
obviously exchanging contracts and schema, not types. If you understand how
this code translates to WSDL, this becomes even more apparent. I also believe
that through data contract versioning you are able to remain autonomous. If
the services we develop today can supply a variety of versions to a variety of
consumers then the service can remain in control of how to handle those various
versions and maintain self-governance.
Message Contracts
One of the types of services that we discussed earlier was a
typed message service. This type of service uses typed messages to invoke a
service and receives typed messages as responses. One of the common needs of
these types of services is to manipulate the format of the actual SOAP message
that is being passed between the client and the service. In the past this would
have to be done with complicated SOAP extensions. With WCF message contracts,
we are able to easily specify the portions of a message that should live in the
SOAP header and those that should live in the body. Often, these two sections
of the message will have different encryption requirements, and the message
contract is a good way to provide flexibility in how we structure that
message. The message contract maps to the message section of WSDL today. The
following is an example of a WCF message contract.
FamilyGuyQuoteRequest.cs
[MessageContract]
public class FamilyGuyQuoteRequest
{
[MessageBody]
public
string characterName;
[MessageHeader(MustUnderstand=True)]
public
string requestedBy;
}
You will notice a couple of interesting things in the simple
message contract defined above. First of all, notice how simple it is to
specify where a member would live in a SOAP message. When using the
[MessageHeader] attribute, I am telling the serializer to expect this field in
the SOAP header. I also have used the MustUnderstand enumeration to tell the
serializer that this is a required field in my SOAP header.
Message contracts round out the primary contract types that
go into specifying exactly what a service will be passing on the wire. This
takes the guesswork out of service definition. As I mentioned above, this is a
vast improvement on the implicit approaches today. We really do not have this
type of flexibility with ASMX. Our current approaches would require us to plug
into the HTTP pipeline and intercept messages using SOAP extensions.
So Am I Truly Contract-First Now?
An interesting ongoing debate amongst those in the industry
that are designing, delivering, and teaching service-orientation is whether or
not a code-first approach will ever allow us to design and deliver services
that are truly loosely coupled, interoperable, and ultimately long-lived. What
we just looked at was a very distinct set of technologies that map directly to
the WSDL contract that is generated when this service is exposed. Were we
actually doing contract-first development with a code-first approach?
The purists would say that contract-first means focusing
on schema (XSD) and behavior (interface) before ever writing any code. What I
would say is that we do this now by specifying data contracts (schema/XSD),
service contract (behavior/interface), and message contracts when necessary.
These approaches are still very code-centric and will require some level of
governance to avoid developers diving right into implementation before a
contract has been agreed upon. However, with the right level of discipline, I
think WCF has found an interesting middle ground for contract-first service
development.