The new System.ServiceModel namespace, which is a core component of Windows Vista, will change the way .NET developers look at building and securing services. Before developers can get going with full-fledged secure Web services, they need to know how to enable one Web service to talk to another Web service, and Microsoft's new Windows Communication Foundation, or WCF, can accomplish that.
It does not take much more than a quick sneak peek inside WCF and its design objectives to see the benefits of its architecture. One of the keys to building a successful application is to build one that endures. While this sounds simple, it is a tall task to fulfill, especially when business environments are so fluid these days. To make matters worse, there is a plethora of technical architecture out there catering best to many different scenarios.
WCF is designed with the primary objective of solving some of those problems by unifying all the Microsoft technology stacks out there, such as EnterpriseServices (COM+), System.Messaging (MSMQ), Interoperable Basic Web Services (ASMX) and the advanced WS-* stack (WSE) with a single programming model. The entire architecture of WCF revolves around the concept of a Message type, which is nothing but an abstract of a wire-level SOAP message, SOAP being a particular XML schema. All the developers know is that solutions they build with WCF will generate SOAP messages on the wire that will interoperate with application solutions from other technology and platform vendors out there.
The Architecture: Layers and Channels
To put it simply, WCF solutions are built in two layers. The bottom layer relates to the messaging infrastructure, which concerns itself with the movement of messages from one point to another. This layer is comprised of classes found within the System.ServiceModel namespace. Several extensibility points can customize the behavior of those classes. Incidentally, the second layer, which is the programming model, builds on top of these extensibility points. It consists of a second set of classes within System.ServiceModel, and they are the primary classes for WCF developers to program against when building WCF applications.
The WCF programming model was designed to achieve a few fundamental objectives. One key objective was to provide a standard interface to the interconnecting networks between the services. In other words, you do not have to do the "modify code, compile, build, test and deploy" cycle if there is a requirement to switch transports or networks, such as between a HTTP transport to a TCP one, or between a network where messages must be encrypted and one where they are not.
Another related key design objective of WCF was for applications and services built on top of it to endure. It accomplishes that by hiding constructs that may turn out to be contemporary artifacts of current Web Services specifications behind programming concepts that exposes real, useful business functionality. Thus, there is a clear abstract boundary between development of business details and the deployment of technical ones. By not mixing business logic code with the technical details, solutions appear more clean and concise and have a better chance of being more agile and therefore surviving in the fluid service-oriented environments of today and tomorrow.
These objectives are accomplished through a layering architecture as a central design principle, which in essence means that WCF is a collection of sub-frameworks that can each be extended or even replaced by developers and partners.
At the 40,000-foot view, WCF consists of two larger frameworks: The Typed layer and the Channel layer.
The fundamental unit of data for the Channel Layer is the Message. Channels are used to Send and Receive Messages. While the Message is a very flexible object, developers will require knowledge of Infosets and XML to fully utilize it. Therefore, what most developers will most likely do is depend on the Typed Layer as their best friend and interface with it to send messages across the wire. The corresponding fundamental unit of data for the Typed Layer is a Common Language Runtime class.
In the Typed Layer, you create CLR objects that implement either Services or Typed Proxies. This layer will then convert parameters and return values into Message objects and method calls into Channel calls. In this way, the Typed Layer builds on and extends the functionality of the Channel Layer, which can then transparently leverage any changes and/or improvements made to Channels. This layering architecture goes a long way in fulfilling the fundamental design objectives I mentioned above.
Let us look at some concrete code to get a better feel of this design.
Show me the Contract
WCF applications consist of services and their clients (consumers). Services define endpoints for consumers to communicate with them. Endpoints comprises of an address, a binding and a contract, which are fondly known as the A, B, Cs of WCF. Briefly, the address is the location of the service, while the binding specifies the protocols and the transports used to communicate with the service.
The contract is what the developer is primarily concerned with. The developer defines the contract, implements it and allows the service to be hosted.
There are a few types of contracts in WCF that can be grouped either under structural or behavioral contracts. Let us define an Operation Contract first:
<OperationContract()> _ Function SubmitCreditCard(ByVal crdcard As CreditCard) As Boolean
An operation contract defines an operation and is a unit of work for the service. It specifies, by default, a request and a reply message exchange pattern (MEP). As you can see from the code sample above, the WCF's concept of an operation contract tightly maps to a Web Services Description Language's (WSDL) definition of an operation, which in turn, corresponds roughly to a CLR method/function.
Next, we look at a simple Service Contract:
<ServiceContract(Namespace:="urn:demos.softwaremaker.net/...")> _ Public Interface IMyFirstSecuredWCFService <OperationContract()> _ Function SubmitCreditCard(ByVal crdcard As CreditCard) As Boolean End Interface
On the same scale, the concept of a Service Contract corresponds roughly to portTypes in WSDL. It is essentially a collection of operation contracts. Incidentally, what I just did above was define a service contract via a contract-first approach. This is done by declaring a .NET interface and then decorating it with the ServiceContract attribute and then defining a class that implements this contract:
'Service class which implements the service contract. Public Class MyFirstSecuredWCFService : Implements IMyFirstSecuredWCFService Public Function SubmitCreditCard(ByVal cc As CreditCard) As Boolean _ Implements IMyFirstSecuredWCFService.SubmitCreditCard Try 'Do some Credit Card Processing and return True if results are positive Catch Return False End Try Return True End Function End Class
This is in contrast to a code-first approach where one can decorate a new or existing class and its methods directly with WCF attributes directly. I would not recommend this, as it tightly binds an interfacing contract with its implementing class in the CLR without a faÇade layer.
Take note that the contract-first approach I described above is slightly different from a WSDL-first approach where you actually author a WSDL file directly instead of letting the underlying platform generate the WSDL file via attributes programming and declaration. Although I would advocate the style of a WSDL-first approach where one can assert more control over the WSDL contract and schema, especially in interoperability scenarios, in the absence of better tools and without developers having to muck around with the specifications, profiles and angle brackets directly, the WCF's definition of a contract-first approach is really a great step forward.
Next up would be to define my custom CreditCard class, which is simply a .NET CLR type:
<DataContract()> _ Public Class CreditCard <DataMember(Name:="AccountName")> Private _AccountName As String <DataMember(Name:="CreditCardType")> Private _CreditCardType As String <DataMember(Name:="CreditCardNo")> Private _CreditCardNo As String <DataMember(Name:="CreditCardExpiryDate")> Private _CreditCardExpiryDate As String Public Property AccountName() As String Get Return _AccountName End Get Set(ByVal value As String) _AccountName = value End Set End Property Public Property CreditCardType() As String Get Return _CreditCardType End Get Set(ByVal value As String) _CreditCardType = value End Set End Property Public Property CreditCardNo() As String Get Return _CreditCardNo End Get Set(ByVal value As String) _CreditCardNo = value End Set End Property Public Property CreditCardExpiryDate() As String Get Return _CreditCardExpiryDate End Get Set(ByVal value As String) _CreditCardExpiryDate = value End Set End Property End Class
The DataContract is a structural contact type in WCF, which is defined by adding DataContract and DataMember attributes to your .NET classes. Both attributes are defined in the System.Runtime.Serialization namespace.
What a Data Contract declaration does is specify how a .NET type is represented in an XML Schema. To see it from another view, two different software entities can have different internal class definitions, yet agree on the same abstract representation of that data. In this case, both sides can translate the data to and from those internal representations into the same physical manifestation of the abstract representation, which can then facilitate communications and interoperability. The XML Schema is most commonly used to compose abstract representations of data to be exchanged between two entities, with XML being the physical manifestation of this abstract representation.
Once all that is set up, it is time to specify the hosting container and then hosting that service.