What is WCF?
In a nutshell, WCF is a
framework for building and hosting services. Hosted by the Microsoft
platform, WCF services make use of standard technologies to offer a wide
range of cross-platform security, transaction, and communication
capabilities.
Before WCF came along, .NET
developers, who built distributed applications had to choose between
communication schemes such as ASP.NET web services, .NET remoting, and
MSMQ. This choice carried with it implications for how the component was
designed, developed, deployed, and consumed. If you went with ASP.NET
web services, you were committing to XML message formats and were
handcuffed by limitations of the HTTP transport protocol. If you chose
.NET remoting, you were able to process messages in an efficient
fashion, but immediately limited yourself to .NET-only service clients.
MSMQ is wonderful for disconnected applications, but in choosing it,
you've eliminated any chance at having a synchronous, request-response
conversation with a software client.
The goal of WCF is to unify these
many technologies and provide a single transport-neutral development
paradigm with common aspects for security, transactions, and exception
handling. The service is implemented independent of the communication
protocol strategy. This is a fairly revolutionary concept that
introduces immense flexibility to service designers. Instead of building
services with tightly coupled and rigid endpoints that do not welcome
change, we can design flexible services that are capable of supporting a
wide range of current and future consumers.
The service endpoint is king, and endpoints in WCF are defined using the easy-to-remember ABC acronym. The letter A stands for addressing, which refers to the actual URL of the service. The letter B stands for binding, which describes how we communicate with the service. Finally, the letter C stands for contract, which defines the operations and data elements that this service exposes. Let's look at each of these in detail.
Defining the contract
Unlike ASP.NET web services,
WCF truly promotes a "contract first" design style where developers
need to thoughtfully consider how the outside world will interact with
their service. There is a clean separation between the interface
definition and the actual implementation of the service. When building
ASP.NET services, the developer typically takes a code-first approach,
where .NET classes are decorated with attributes and exposed as
services. In the WCF model, we focus first on the data being shared and
what our interface to the outside world should look like (i.e. the
contract). Only after this critical step is complete does the WCF
developer begin to design the actual service implementation logic.
There are actually three different contracts you may define for a WCF service. These are:
Service contract
Data contract
Fault contract
Service contracts
The service contract
explains what your service can do. It's built using a .NET interface
class and decorated with WCF attributes that identify it as a service
contract. A basic service contract looks like this:
[ServiceContract()]
public interface IVendorContract
{
[OperationContract()]
void InsertVendor(string vendorId, string vendorName);
[OperationContract()]
bool DeleteVendor(string vendorId);
}
Notice that the interface has a ServiceContract attribute and each operation that we wish to expose publicly on our contract has an OperationContract
attribute. Each of these metadata attributes has a series of optional
parameters that let us explicitly define public characteristics of the
service. For instance, we can add the Name and properties to the ServiceContract to better characterize this service in our environment. We can also add a series of properties to the OperationContract SOAPAction
value is set to. Why give an alternate name to a service operation?
Consider scenarios where you have an overloaded operation in your WCF
service contract, and need each WSDL operation to have a unique public
name. C# (and .NET) support overloading, but the WSDL standard no longer
does. Namespace to control what the operation is named and the
[ServiceContract(Name="VendorService", Namespace="http://Seroter.BizTalkSOA/Contracts")]
public interface IVendorContract
{
[OperationContract(Name="InsertVendor")]
void InsertVendor(string vendorId, string vendorName);
[OperationContract(Name="InsertVendorWithContact")]
void InsertVendor(string vendorId, string vendorName, string vendorContactName);
[OperationContract(Name="DeleteVendor")]
bool DeleteVendor(string vendorId);
}
Data contracts
As you can probably imagine,
services often need to accept and return comprehensive data entities in
addition to simple type parameters. I might want to model a data entity
such as a customer instead of having a service operation accept 15 individual string parameters. Complex data parameters are categorized as data contracts in WCF. A data contract is a .NET class object decorated with the DataContract attribute and whose public properties are flagged with DataMember attributes. Public service operation definitions can only include complex types identified as data contracts.
[DataContract()]
public class VendorType
{
private string vendorId;
private string vendorName;
private string vendorContactName;
[DataMember()]
public string VendorId
{
get { return vendorId; }
set { vendorId = value; }
}
[DataMember()]
public string VendorName
{
get { return vendorName; }
set { vendorName = value; }
}
[DataMember()]
public string VendorContactName
{
get { return vendorContactName; }
set { vendorContactName = value; }
}
}
Much like the service contract, the attributes of the data contract
allow for more fine-grained control of the entity definition. For
instance, we may provide a Name and Namespace to the DataContract, while also adding some useful node ordering and existence attributes to the member elements.
[DataContract(Name="Vendor" Namespace = "http://Seroter.BizTalkSOA/Types")]
public class VendorType
{
private string vendorId;
private string vendorName;
private string vendorContactName;
[DataMember(IsRequired=true, Order=0)]
public string VendorId
{
get { return vendorId; }
set { vendorId = value; }
}
[DataMember(IsRequired=true, Order=1)]
public string VendorName
{
get { return vendorName; }
set { vendorName = value; }
}
[DataMember(IsRequired=false, Order=2)]
public string VendorContactName
{
get { return vendorContactName; }
set { vendorContactName = value; }
}
}
If you omit the Order property from the DataMember attribute, then the nodes are ordered alphabetically, which may not be how you wish to organize your public schema.