
February 23, 2006
This is part one of a two-part series.
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 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 |
Table 1
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
|
Table 2
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
|
Table 3
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
|
Table 4
The DataContract is a structural contract 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.
Playing the Perfect Host
In my examples here, I will show briefly show to self-host the service in a Windows Console as well as host it in Microsoft's web service, Internet Information Server (IIS).
For a Windows console application, it is as simple as declaring a type that I would like to host within a WCF ServiceHost and opening it for the service to be a passive digital
nerve ending that listens for incoming messages. Opening up a ServiceHost actually opens and sets up a bunch of listener factories on the service side, which is the equivalent of the channel
factories on the consumer side. The code in this table will open and set up a WCF service inside of a console application:
Sub Main()
Using svcHost As New ServiceHost(GetType(MyFirstSecuredWCFService))
svcHost.Open()
Console.WriteLine("My First Secured WCF Service is running ...")
Console.ReadLine()
svcHost.Close()
End Using
End Sub
|
Table 5
To port the business code over to IIS, since IIS is the service host, we just simply need to declare an entry point to act as the listening endpoint. In WCF, the default is the .svc file.
In our example, I will create a physical index.svc file and declare the service as such:
<%@Service language=vb Debug="true" class="MyFirstSecuredWCFService" %>
|
Table 6
As you can infer from this, my business code remains constant despite the change in hosting containers.
Next, we will briefly examine the deployment details of the service. Since delving deeply into addresses and bindings is not the focus of this article, I will just show very simply how an
address and its bindings can be defined in a configuration file easily. This can be configured through code as well but with the compromise of a tremendous amount of flexibility. Just
imagine: if an endpoint address changes, an administrator would be able to alter the specification of that endpoint without any code changes and recompilation.
The binding section would be explained in the security section later. What the table below shows is how the service and contract are all wired up together with its listening address.
In other words, this configuration details merely refers to the abstract contract drawn up by the developer and then identify the assembly in which it is located and puts a well-known
communicating endpoint address to it:
<system.serviceModel>
<services>
<service type="MyFirstSecuredWCFService, WebApp">
<endpoint address="http://localhost:2088/MyFirstSecuredWCFService"
contract="IMyFirstSecuredWCFService, WebApp" binding=... />
</service>
</services>
…
</system.serviceModel>
|
Table 7
Take note of how I wire up the contract (the second table) and its implementation class (the third table), referred here as a service type together with an endpoint address.
For an IIS-hosted application, the endpoint address should be defined by the physical *.svc file and the name of its hosting virtual directory and should have an empty value unless you
would like to give it an extension and assign a different set of bindings to it. For example, if the endpoint address="", then the actual communication endpoint is
http://yourSite/yourDirectory/yourfile.svc. If I set the endpoint address="secure" and then bind it the same contract but with a different set of bindings (a secured one, for
example), the consumer would have to navigate to http://yourSite/yourDirectory/yourfile.svc/secure to initiate a secured message exchange with the service. Do not let this uri throw
you off, although slightly unnatural, it is not wrong. It is just the way IIS and the entire ASP.NET / ASMX architecture is set up which defines a physical file (i.e. *.asmx) as the focal
entry point.
Do keep in mind that what I have been talking about here are WCF endpoints and not physical network nodes. One same physical network node can host any number of WCF endpoints and hence
any number of contracts on that node. That, however, will be another topic for another day.
It takes two to tango
A service is passive and does nothing by itself. That is why there is an endpoint address attached to it to act as the entry point for the outside world. The final piece of the puzzle
after the service is set up properly is to build and configure the consuming client.
Now, what we have is a command prompt utility called the svcutil.exe to build the proxy and the client corresponding configuration file based on the published service's metadata (WSDL
+ Schema + Policy). It will be the goal of the WCF team to have this client metadata consumer integrated into future versions of Visual Studio. It should be something resembling the effect
of an existing artifact called "Add Web Reference" or what I would ultimately prefer – "Add Service".
Once the service is up, running and listening, we run svcutil.exe against the service endpoint address. For example, if it were an IIS-hosted service, it would be svcutil.exe
https://localhost/Softwaremaker/Projects/index.svc while if I run it against a console-hosted one, it could look something like svcutil.exe
http://localhost:2088/MyFirstSecuredWCFService. It all depends on how I set up the virtual directory host or how and what I specify the address to be in code or config.
The output will be two files.
1. A generated typed WCF proxy in a preferred language that translates method calls to dispatched message
2. An app.config file for the client implementation that corresponds with the technical deployment details of the configuration file of the service side.
After that, it will just be setting up the client code to make calls against the generated typed WCF proxy:
Sub Main()
Console.WriteLine("Press enter when the Credit Card Service is running ...")
Console.ReadLine()
Dim pxy As New MyFirstSecuredWCFServiceProxy
Dim cc As New CreditCard
cc.AccountName = "William Tay"
cc.CreditCardExpiryDate = "08082008"
cc.CreditCardNo = "123456780"
cc.CreditCardType = "AMEX"
Console.WriteLine(pxy.SubmitCreditCard(cc))
Console.ReadLine()
End Sub
End Module
|
Table 8
While I admit the code shown above has a slight tinge of RPC-flavor to it, make no mistake about it -- WCF is messaging and messages right to its core. The above example is a simple,
straightforward approach that most developers today can identify with.
The resultant dispatched message on the wire is a nicely formatted SOAP message and the WCF service will respond with a result of the same format:
<s:Envelope xmlns:s="…">
<s:Header />
<s:Body>
<SubmitCreditCard xmlns="http://demos.softwaremaker.net/MyFirstSecuredWCFService">
<crdcard xmlns:i="…" xmlns:x="…" xmlns:a="…">
<a:AccountName>William Tay</a:AccountName>
<a:CreditCardExpiryDate>08082008</a:CreditCardExpiryDate>
<a:CreditCardNo>123456780</a:CreditCardNo>
<a:CreditCardType>AMEX</a:CreditCardType>
</crdcard>
</SubmitCreditCard>
</s:Body>
</s:Envelope>
|
Table 9
Read part two of this series, "Securing your WCF Service".
Authors
 |
William Tay is a Senior Consultant/Enterprise Software Architect with NCS Pte. Ltd., based in Singapore. He is a Microsoft Most Valuable Professional (MVP) for Solutions Architect. You can read about his views on software architectures, XML, Web services and other technical ramblings via his technical blog at http://www.softwaremaker.net/blog.
|
|