This chapter will be your introduction to the programming model of WCF. We will start
by reviewing the principals of a Service Oriented Architecture and how WCF supports
those principals. Then we will also describe some practical deployment scenarios for
WCF in distributed enterprise systems and then summarize some of the fundamental
WCF concepts that will be discussed first in this chapter and then elaborated on
throughout this book.
Windows Communication Foundation (WCF), formerly code-named "Indigo," is a
new distributed messaging platform released with Windows Vista as part of the .NET
Framework 3.0. The .NET Framework 3.0, formerly code-named "WinFX," includes
four pillars: Windows Presentation Foundation (WPF), Windows Workflow Foundation
(WF), Windows Communication Foundation (WCF), and Windows CardSpace.
As Figure 1-1 illustrates, .NET 3.0 relies on the .NET Framework 2.0 (.NET 2.0) and is
supported on Windows XP, Windows Vista, Windows Server 2003, and Windows
"Longhorn" Server platforms.
Why release yet another technology for distributed messaging? Unlike its predecessors,
WCF is a truly service-oriented, loosely coupled, and interoperable platform. It
simplifies service-oriented system design by removing the design dependencies that
traditionally exist between how you access business functionality and the actual
implementation of that business functionality. WCF promotes loose coupling not
only between services and the functionality they expose, but also for choice of protocol,
message encoding formats, and hosting environment. For example, services can
be accessed over a variety of supported protocols, including named pipes, TCP,
HTTP, and MSMQ. WCF also supports all of the core and emerging web service
standards, which makes it a highly interoperable platform. Messages can always be
represented in a format consistent with a set of well-adopted standards to communicate
with other platforms.
Besides these modern characteristics, what’s even more interesting is that you can
now choose a single communication stack to gain access to the system services necessary
to build a distributed system. WCF unifies the disparate programming paradigms
you have previously used on the Windows platform to achieve similar goals—
namely .NET Remoting, ASP.NET Web Services (ASMX), and Enterprise Services.
WCF provides all of the plumbing for security, transactions, and reliable messaging
over any protocol. Only Enterprise Services came close to providing all of these features
in a single stack, but your component design was coupled to the technology
and limited to TCP communication (thus, not interoperable).
This chapter will be your introduction to the programming model of WCF. I’ll start
by reviewing the principals of a Service Oriented Architecture and how WCF supports
those principals. I’ll also describe some practical deployment scenarios for
WCF in distributed enterprise systems and then summarize some of the fundamental
WCF concepts that will be discussed first in this chapter and then elaborated on
throughout this book. After introducing core WCF concepts, I’ll walk you through
labs that exercise certain techniques and features. Instead of boring you with a bunch
of "Hello World" examples, I plan to kick things up just a notch in this introductory
chapter by enforcing good practices from the start while I teach you core concepts.
Each of the labs you complete in this chapter will become successively more complex,
and each will expose a new layer of detail. After each lab I will explain the relevant
techniques and features you applied, discuss their relevance, and comment on
recommended practices.
The labs in this chapter will cover the following topics:
Manually creating and consuming WCF services without the help of Visual Studio
templates and related tools. This will provide you with a picture of the bare
necessities you need to create, host, and consume a WCF service so that you
understand the underlying programming model.
Creating WCF services using various Visual Studio project and service templates,
leveraging configuration tools, and generating code to consume services.
Approaches to assembly allocation and service hosting over various protocols.
The importance of service metadata for publishing and consuming services.
By the end of this chapter you will be familiar with many core concepts including
service contracts, endpoints, bindings, behaviors, hosting, metadata, channels, and
proxies. Of course, throughout this book, the same concepts as well as additional
ones will be discussed at length as you dive more deeply into specific WCF features.
SERVICE ORIENTED ARCHITECTURE
What is Service Oriented Architecture (SOA)? There have been so many interpretations
of this throughout the years that it seems important to establish a common
understanding before I discuss WCF as an SOA platform. The Organization for the
Advancement of Structured Information Standards, better known as OASIS (http://
www.oasis-open.org), provides this official definition in its Reference Model for Service
Oriented Architecture:
Service Oriented Architecture (SOA) is a paradigm for organizing and utilizing distributed
capabilities that may be under the control of different ownership domains.
You can find the OASIS SOA Reference Model documentation at http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=soa-rm.
You might add to this definition by stating that SOA relies on the ability to access
chunks of business functionality, potentially owned by different applications, departments,
companies, or industries. Notice that this description does not specify the
mechanism through which those chunks of functionality are accessed. In fact, the
term "service" isn’t even mentioned, although it is implied.
From OOP to SOA
The road to SOA has been a progressive one—driven by the need to improve how
developers build complex enterprise systems. The principals behind enterprise system
design are far-reaching: from object-oriented programming to component-oriented
programming to service-orientation. All three approaches share the common
goal of encapsulation and reuse. With object-oriented programming, classes encapsulate
functionality and provide code-reuse. To share classes between distinct applications
or binaries, however, you have to copy the code, as shown in Figure 1-2.
Component-oriented programming introduces the concept of sharing binaries that
encapsulate reusable classes. Initially, this was limited to the same machine until distribution
was made possible with technologies like COM and DCOM, CORBA, and
later Enterprise Java Beans (EJBs) and .NET Remoting. Although these technologies
accomplish distribution in different ways the result is the same—binary components
are activated over process and machine boundaries (see Figure 1-3).
Component-oriented programming has many limitations, but the most obvious is
tight coupling to a specific technology. How can a Java client call a COM component?
How can a .NET assembly invoke an EJB? It all boils down to protocols and
messaging formats. Invoking a remote component of any kind requires serializing a
message and passing it across applicable process or machine boundaries. Bridge technologies
and adapters exist to transform messages from one technology into another,
so that when the message arrives it can be understood and processed. The reverse
happens as responses are fed back to the caller. This approach is cumbersome, however,
sometimes introducing multiple transformations between clients and components—
and sometimes not even possible. Instead of exposing components directly,
components can be accessed through service boundaries to alleviate some of this
pain (see Figure 1-4).
So, does service-orientation solve the problems inherent to component-oriented
programming? It depends on where you sit on the meaning of service-orientation. I
would definitely agree that in its purest form, service-orientation delivers a solution
to these problems by introducing (via web services) the concept of contracts,
policies, and interoperability. In that respect, applications can communicate with
one another’s services, as shown in Figure 1-5, without concern over the technology
each employs. But you could also argue that service-orientation is an approach
to development that implies the encapsulation of business components, data
access, and data storage such that access is controlled through a top-level entry
point. The package is a service, accessible over whatever protocols are supported,
even if it lacks interoperability.
No matter how you define a service, the underlying point here is that to build an
enterprise system, developers must be able to distribute chunks of functionality
across process and machine boundaries to deal with performance bottlenecks, to
introduce security boundaries, and to facilitate reuse. In addition, these chunks of
functionality may be important to expose to other applications in a corporate ecosystem—
which implies the potential need for interoperability on top of the rest.
What Is a Service?
This is an important question—and the answer varies depending on the context of
the discussion. For example, a service is a logical term to SOA, but it has physical
meaning to WCF. I’ll focus on the former in this section. According to the high-level
definition of SOA, business functionality must be distributable and accessible in
some way. The term service in this case refers to the entry point or "window"
through which business functionality can be reached. Consider the application architecture
illustrated in Figure 1-6. The client application represents an Agency Management
System that includes many chunks of business functionality such as Certificate
Issuance, General Ledger, CRM, and Reporting. In Figure 1-6, the client application
coordinates access to these features by consuming business components directly. In
this case, components are not distributable in such a way that they can be location
transparent, thus they are not services.
So, what constitutes a service in SOA terms? It could be a serviced component
exposed using Enterprise Services, a .NET Remoting component, an ASMX web service,
or a WCF service. Any of these technologies can be useful in exposing the business
logic in such a way that the client can reach that functionality at remote
locations in a distributed environment, without communicating directly with business
components. Figure 1-7 illustrates the same services beneath the Agency Management
System example from Figure 1-6, but this time each feature is exposed via
one of the aforementioned technologies. Serviced components are reached using
DCOM over TCP, .NET Remoting components via RPC over TCP, ASMX web services
via SOAP over HTTP, and WCF services via SOAP over any protocol.
The point is that services are not necessarily web services—they are merely chunks of
business functionality exposed in some way such that they respect the tenets of SOA.
Tenets of SOA
Although there is no official standard for SOA, the community seems to agree on
four basic tenets as the guiding principles for achieving an SOA. They are:
Service boundaries are explicit.
Services are autonomous.
Clients and services share contracts, not code.
Compatibility is based on policy.
Let’s look at each of these in greater detail.
Service boundaries are explicit
Services are responsible for exposing a specific set of business functionality through a
well-defined contract, where the contract describes a set of concrete operations and
messages supported by the service. Services completely encapsulate the coordination
of calls to business components in response to operations it exposes to clients, as
Figure 1-8 illustrates. Implementation details behind the service are unknown to clients
so that any technology platform could be invoked behind the service without
impact to the client. In addition, the location of the service isn’t important to the client
as long as the client knows where to reach it.
Enterprise Services, .NET Remoting, ASMX, and WCF all support this tenet. With
Enterprise Services and .NET Remoting, the boundary and contract are defined by
the public operations of the serviced component or remote component, respectively.
In the case of Enterprise Services, the contract is described as a type library, while
with .NET Remoting the contract is a shared CLR interface. As for ASMX and WCF,
contracts are described in Web Services Description Language (WSDL), an interoperable
standard. All of these technologies also support location transparency in one
respect or another. That is, the contract is independent of the location of the service
in all cases.
Where WCF improves on earlier technologies in support of explicit
boundaries is in the way contract design and deployment are handled.
With WCF, you explicitly define the contract and opt-in every operation
and data element that you intend to expose publicly. WCF also
goes beyond location transparency with protocol transparency, meaning
you can expose services over any number of protocols.
Services are autonomous
Services encapsulate business functionality, but they must also encapsulate other
dependencies of the business tier. In this way the entire service should be moveable
or even replaceable without impact to other services or system functionality as illustrated
in Figure 1-9. As I mentioned before, a service represents a major chunk of
business functionality that owns its own business components, data access components
and data storage if applicable. It should be able to perform key functions without
external dependencies. This is what is meant by atomicity.
Part of atomicity also dictates the following:
The service boundary must act as an independent unit for versioning. Changes to
business components may require versioning the service contract, for example.
The service boundary is the deployment boundary for callers.
The service must operate in isolation and be fault-tolerant. That is, exceptions
behind the service tier should not impact other services.
Atomicity is largely influenced by design, but WCF does enable atomicity
by providing a clear approach to contract versioning, a flexible
approach to deployment, and certainly handles fault isolation if services
are hosted by the same process.
Clients and services share contracts, not code
Given the first SOA tenet, that service boundaries are explicit, it only makes sense
that this boundary be the law as far as how clients interact with services. That means
that the contract must not change once published, or must at a minimum remain
backward compatible to existing clients—and this requires discipline.
In theory, contracts are not tied to a particular technology or platform, but this is not
actually an official requirement of SOA—only a strong tendency. Thus, you could
say that serviced components, ASMX web services, and WCF services all support
this tenet since they all are capable of publishing a contract that is consumed by clients
without sharing code (type libraries or WSDL, respectively). This is where .NET
Remoting falls down, since it relies on sharing CLR types, a .NET-specific construct.
The beauty of WCF is that it uses interoperable contract definitions
(WSDL) for all types of services—regardless of the communication
protocols used to reach those services.
Compatibility is based upon policy
While contracts describe the business functionality available at a service boundary,
policy describes other constraints, such as communication protocols, security
requirements, and reliability requirements. Enterprise Services and .NET Remoting
don’t really have a way to publish such policy requirements, but ASMX with Web
Services Enhancements (WSE) and WCF do. Policy is actually an extension to
WSDL that can describe access constraints in a way that clients can be aware of them
and invoke services in a compatible manner.
WCF support for policy is completely hidden from the developer—it
is automatically included with the WSDL document based on how
you configure WCF service for features such as security and reliability.
Big SOA, Little SOA
The problem with discussing the tenets of SOA in the strictest sense is that levels of
compliance may vary based on the scenario. On the one hand, SOA is a big business
buzzword tossed into conversations at board meetings, at executive briefings, and in
hallway conversations between C-level executives. At this level, however, SOA really
refers to connecting disparate systems across application, department, corporate,
and even industry boundaries. This is what I call Big SOA. The other use for the term
SOA is to describe how applications are designed as chunks of business functionality
that are isolated behind explicit service boundaries. I call this Little SOA.
Big SOA is an Enterprise Architect (EA) activity. The EA cares about connecting heterogeneous
systems that may originate from different vendors. For example, you can
connect HR, Payroll, CRM, and possibly other applications across the organization
to achieve a business goal. In some cases, it is even useful to control messaging
between systems and track usage with an Enterprise Service Bus (ESB)—a term that
also means many things, but in this case I refer to the ability to pass all messaging
through a common service for tracking and routing purposes. In short, Big SOA is
about connecting entire systems through their respective service boundaries.
Little SOA is a Software Architect (SA) activity. The SA cares about designing a system
that may encapsulate functionality behind service boundaries to achieve reuse,
maintainability, version control, visibility, orchestration, and other benefits. These
services may never see the light of day outside the application to which they belong.
On the other hand, some internal application services may also be exposed for public
access to facilitate communications and interoperability with other applications.
If applications don’t expose public services, it becomes a challenge to
connect applications.
Little SOA enables Big SOA.
The distinction between approaches in SOA is important because of the level of
strictness in applying SOA tenets. For example, it isn’t always possible to completely
isolate business components, business entities, and data tiers between services in the
same system. Data is usually highly relational within a system, such that different
areas of business functionality share common data stores and entities. Figure 1-10
illustrates an application with three services: Accounts, Customers, and Reporting.
Accounts Service and Customers Service each expose operations to their respective
types, but Accounts data is related to Customers in the system; thus, there isn’t a
pure separation between the tables required to support each service. At the same
time, both Accounts and Customers also provide access to business functionality and
CRUD operations (Create, Read, Update, Delete) that can be considered completely
independent of one another—thus the need for separate services. The Reporting Service,
in fact, needs to access all tables to aggregate results.
In a pure SOA play, each service would have sole ownership over its data tables, and
services would have to communicate with one anotehr to access those tables, even for
reporting. This can create unnecessary overhead and complexity within a system.
Instead, Figure 1-10 illustrates a way to support sharing relational tables behind the
service boundary by coordinating relational results at the database, possibly via stored
procedures. This way, the vertical assemblies associated with a service are completely
owned by the service, and if a particular service, such as Reporting, requires access to
multiple relational tables that are also accessed by other services, the data access layer
coordinates this result for the service. While serialized business entities may be shared
between services, business and data access components are not. Services can always
call other downstream services to share functionality when service isolation is clear cut
and the overhead of the service call makes sense—for example when services provides
core functions such as document generation or messaging.
The point is that not all four tenets of SOA can be followed to the letter when designing
services within an application. When application services are exposed to Big SOA
in most cases the entire application is deployed with the service, thus the shared entities
and data stores are implied parts of the atomic service.
In this book, I’ll be focusing on how you deploy WCF services as part
of an enterprise application. In other words, Little SOA.
WCF SERVICES
WCF services are the new distributed boundary in an enterprise application—with
an emphasis on SOA. In the past, you had to deliberate between Enterprise Services,
.NET Remoting, or ASMX to distribute and reuse functionality, WCF provides you
with a single programming model to satisfy the needs of any equivalent distribution
scenario. With WCF, you can cross process, machine, and corporate boundaries
over any number of protocols; you can expose interoperable web services; and you
can support queued messaging scenarios. I’ll take you through a few examples where
WCF is deployed in lieu of earlier technologies.
Figure 1-11 illustrates an intranet scenario where a WCF service is invoked within an
application domain over TCP protocol. In this scenario, the client needed to reach
remote services and authenticate with Windows credentials, and didn’t require
interoperability. As such, the service is accessible over TCP using binary serialization
for better performance, and supports traditional Windows authentication using
NTLM or Kerberos. In the past, this may have been achieved using Enterprise Services
(or possibly .NET Remoting although security features are not built-in).
Figure 1-12 illustrates an Internet scenario where multiple web services are
exposed—one supporting legacy protocols (Basic Profile), another supporting more
recent protocols (WS*). With WCF, a single service can be defined and exposed over
multiple endpoints to support this scenario.
In Figure 1-13 you can see WCF implemented at several tiers—behind the firewall to
support an ASP.NET application, and outside the firewall for smart client applications.
Again, the same service can be exposed over multiple protocols without duplicating
effort or switching technologies.
Throughout this book, I’ll be exploring these and other scenarios while discussing
specific features of the WCF platform.
FUNDAMENTAL WCF CONCEPTS
At its core, WCF is a development platform for service-oriented applications. As I
mentioned earlier, WCF is part of the .NET Framework 3.0, which comprises a set
of new assemblies that rely on the .NET Framework 2.0. System.ServiceModel is the
assembly that contains core functionality for WCF, which explains why the WCF
platform is often called the I. Any project that exposes or consumes
WCF services must reference the System.ServiceModel assembly, and possibly other
supporting assemblies.
Before you can begin to do interesting things with the service model, it helps to
understand the core features that make it possible to create, host, and consume services.
In this section, I will briefly summarize some of the concepts that will be elaborated
on in this chapter, to help you on your way.
Message Serialization
All enterprise applications at some point must make remote calls across process and
machine boundaries. This is handled by sending messages between applications. The
format of the message is what determines an application’s ability to communicate
with other applications. Remote procedure calls (RPC) and XML messaging formats
are two common ways for applications to communicate.
RPC calls are used to communicate with objects (components) across boundaries—
for example, calls from a client application to a remote object living in another process.
RPC calls are marshaled by converting them to messages and sending them over
a transport protocol such as TCP. When the message reaches its destination, it is unmarshaled
and converted into a stack frame that invokes the object. This is all usually
handled transparently through a client proxy and stub at the destination. Both
proxy and stub know how to construct and deconstruct messages. This process is
known as serialization and deserialization.
Figure 1-14 illustrates the serialization and deserialization process (often just called
serialization) from a high level. As I mentioned, the transport carries a message
according to the agreed-upon format of both the client and remote application. As
far as the client is concerned, it is usually working with a proxy that looks like the
remote object. When a method is invoked at the client, the proxy invokes underlying
plumbing of the technology (for example, Enterprise Services or .NET Remoting)
to serialize the outgoing message. The remote application usually listens for
messages on a particular port, and as they arrive, deserializes those messages (using
the same technology) to build a stack frame and invoke the appropriate method on
the remote object. The method return is likewise serialized by the underlying plumbing
and returned to the client, at which time the plumbing deserializes the message
and constructs a stack frame for the response. RPC communication like this comes in
many flavors, and each flavor is generally not compatible with another—that’s why
RPC is not interoperable.
To achieve interoperability, systems rely on a standard format for messages understood
by both ends of the communication. Applications still exchange messages, but
they are formatted in XML according to known protocols. The technology used to
support this is traditionally associated with web services such as ASMX, WSE, or
WCF. As Figure 1-15 illustrates, the serialization process is consistent with RPC, the
key difference being the underlying plumbing, the format of the message, and the
target object, which is usually a web service. The other difference is in the lifetime of
the service. While remote objects are frequently kept alive for the duration of a client
session, web services are typically constructed anew for each call.
WCF can be used to achieve both RPC-style messaging and web service messaging.
In both cases, the type that ultimately processes messages is a service type, but its
lifetime can be controlled to behave like traditional client-server components or like
web services without the notion of a session. The service model handles all serialization
activities based on configuration settings.
Services
WCF applications expose functionality through services. A service is a Common Language
Runtime (CLR) type that encapsulates business functionality and exposes a set
of methods that can be accessed by remote clients. In order for a regular CLR type to
be considered a service it must implement a service contract.
A service contract is defined by applying the ServiceContractAttribute to a class or
interface. When applied to a class, the class becomes a service type. When applied to
an interface, any class that implements the interface becomes a service type. In either
case, methods exposed by the class or interface must be decorated with the
OperationContractAttribute to be considered part of the service contract. Methods
with this attribute are considered service operations.
A service type must be hosted before clients can invoke service operations.
Hosting
Service functionality is made available at runtime through a host process—any managed
process will do the trick. Many hosting options are available for WCF services,
including:
Self-hosting
This includes console applications, Windows Forms or WPF applications, or
Windows services.
Internet Information Services (IIS)
Services can be hosted alongside other ASP.NET applications, for example.
Windows Activation Service (WAS)
This is similar to IIS hosting but is only available to IIS 7.0.
Each hosting environment has its benefits and appropriate uses, which
will be discussed in Chapter 4.
Although a host process is important, ultimately it is the service model that knows
how to process messages targeting a service. For this a ServiceHost instance is associated
with each service type. ServiceHost is part of the service model and is responsible
for initializing communication channels that receive messages to a service.
Basically, to host any service, you construct a ServiceHost, provide it with a service
type to activate for incoming messages, provide it with one or more addresses where
the service can be located along with the service contract supported by each address,
and provide it with the supported communication protocols.
You can think of the ServiceHost as responsible for managing the lifetime of the
communication channels for the service.
Endpoints
When the ServiceHost opens a communication channel for a service, it must expose
at least one endpoint for the service so that clients can invoke operations. In fact,
endpoints are the key to invoking service functionality. An endpoint describes where
services can be reached, how they can be reached, and what operations can be
reached. Thus, endpoints have three key parts:
Address
Refers to the URI where messages can be sent to the service.
Binding
Bindings indicate the protocols supported when messages are sent to a particular
address.
Contract
Each address supports a specific set of operations, as described by a service
contract.
The ServiceHost is provided with a list of endpoints before the communication channel
is opened. These endpoints each receive messages for their associated operations
over the specified protocols.
Addresses
Each endpoint is associated with an address, identified by a URI. An address has a
scheme, domain, port, and path in the following format: scheme://domain[:port]/
[path].
The scheme indicates the transport protocol being used, such as TCP, named pipes,
HTTP, or MSMQ. Respectively, the schemes for these protocols are net.tcp, net.pipe,
http, and net.msmq. The domain refers to either a machine name or web domain.
Sometimes localhost is used for communications on the same machine. The port can
be specified to use a specific communication port other than the default for the protocol
identified by the scheme. For example, HTTP defaults to port 80. Here are
some examples of valid base addresses before specifying a path:
A path is usually provided as part of the address to disambiguate service endpoints.
The path does not usually include a filename for self-hosting, but with IIS (as you
will see later in this chapter) a physical file is implicitly included in the address.
These are valid self-hosting addresses that include paths:
When you add endpoints to a ServiceHost instance, you must specify a unique
address for each endpoint. That means that you must vary at least one of the scheme,
domain, port, or path specified.
Bindings
A binding describes the protocols supported by a particular endpoint, specifically, the
following:
The transport protocol, which can be TCP, named pipes, HTTP, or MSMQ
The message encoding format, which determines whether messages are serialized
as binary or XML, for example
Other messaging protocols related to security and reliability protocols, plus any
other custom protocols that affect the contents of the serialized message
There are a number of predefined bindings (called standard bindings) provided by the
service model. These standard bindings represent a set of typical protocols representative
of common communication scenarios. Bindings are discussed in detail in
Chapter 3.
Metadata
Once the ServiceHost is configured for one or more endpoints, and communication
channels are open, service operations can be invoked at each endpoint. This is
according to the protocols supported by each endpoint. Clients invoke service operations
at a particular endpoint. To do so, they need information about the endpoint,
including the address, the binding, and the service contract. Information about service
endpoints is part of the metadata for a particular service. Clients rely on this
metadata to generate proxies to invoke the service.
Metadata can be accessed in two ways. The ServiceHost can expose a metadata
exchange endpoint to access metadata at runtime, or it can be used to generate a
WSDL document representing the endpoints and protocols supported by the service.
In either case, clients use tools to generate proxies to invoke the service.
You’ll explore different ways to work with service metadata throughout
this chapter, and Chapter 2 discusses metadata in further detail.
Proxies
Clients communicate with services using proxies. A proxy is a type that exposes
operations representative of a service contract that hides the serialization details from
the client application when invoking service operations. For WCF applications,
proxies are based on the service contract, so if you have access to the service contract
definition, you can create a proxy instance to invoke the service. Before the
proxy instance can be used to call service operations, it must be provided with information
about one of the endpoints exposed for that service contract—there is a oneto-
one relationship between proxy and endpoint.
Tools also exist to generate proxies and endpoint configurations from metadata. In
this chapter, you will learn how to create a proxy manually, without generating
metadata, and how to use proxy generation tools. In either case, the client must open
a communication channel with the service to invoke operations. This channel must
be compatible with the channel exposed by the ServiceHost for communications to
work.
Channels
Channels facilitate communication between clients and services in WCF. The
ServiceHost creates a channel listener for each endpoint, which generates a communication
channel. The proxy creates a channel factory, which generates a communication
channel for the client. Both communication channels must be compatible for
messages between them to be processed effectively. In fact, the communication channel
is comprised of a layered channel stack—each channel in the stack is responsible
for performing a particular activity while processing a message. The channel stack
includes a transport channel, a message-encoding channel, and any number of message
processing channels for security, reliability, and other features. Without getting
into specifics, the binding controls which channels participate in the channel stack,
as shown in Figure 1-16. (The details of channels and bindings are explored in
Chapter 3.)
Behaviors
Behaviors also influence how messages are processed by the service model. While
services and endpoints determine the core communication requirements and metadata
shared with clients, a behavior modifies the way messages are processed as they
flow through the channel stack. Behaviors are local to the client or service—thus,
they are not included in metadata.
There are behaviors to control many service model features such as exposing metadata,
authentication and authorization, transactions, message throttling, and more.
Behaviors are enabled either in configuration or by applying behavior attributes to
client proxies and services.
In this chapter, you’ll learn how to apply the metadata behavior to a
service, but other behaviors will be explored throughout this book as
they relate to each feature.
CREATING A NEW SERVICE FROM SCRATCH
You’re about to be introduced to the WCF service. This lab isn’t your typical "Hello
World"—it’s "Hello Indigo"! In this lab, you will learn how to build a new WCF service
and in the process learn the minimum requirements of service development and
consumption. Here’s a short list of things you’ll accomplish:
Create a new service contract and service implementation
Programmatically configure a service host, its endpoints, and bindings
Create a client application and open a client channel proxy to invoke the service
Now, before you start thinking "been there, done that," this simple lab will be
slightly different because I’m going to give you some practical design tips that ensure
configurability and appropriate decoupling of service, host, and client. In addition,
I’ll be diving deeper into basic concepts such as services, service contracts, endpoints,
bindings, ServiceHost, and channels.
Lab: Creating Clients and Services Programmatically
In this first lab, you will create a new solution with three projects: a service, a host,
and a client. When you run the service host, you’ll expose a single service endpoint.
The client application will access service operations through that endpoint. You’ll
host the service in a console application and invoke the service using a manually constructed
proxy. This lab will teach you the basic requirements for creating, hosting,
and consuming a service with WCF.
Creating a new service
The first thing you will do is create a new service contract with a single operation and
implement this contract on a new service type.
In this lab, everything begins from scratch, so you’ll start by creating a new
Visual Studio solution. Open a new instance of Visual Studio 2005. Select File ->
New -> Project, and from the New Project dialog, create a new Blank Solution in
the <YourLearningWCFPath>\Labs\Chapter1 directory. Name the solution
ServiceFromScratch. Click OK to create the empty solution.
Create the service project. From Solution Explorer, right-click on the solution
node and select Add -> New Project. Select the Class Library template, name the
project HelloIndigo, and make sure the location path matches the solution at
<YourLearningWCFPath>\Labs\Chapter1\ServiceFromScratch. Click OK to create
the new project.
Now you will create your first service contract. From Solution Explorer, rename
the project’s only class file to Service.cs. Open this file in the code window.
Add a new interface named IHelloIndigoService in Service.cs. Add a single
method to the interface, HelloIndigo, with the signature shown here:
public interface IHelloIndigoService
{
string HelloIndigo( );
}
Add a reference to the System.ServiceModel assembly. From Solution Explorer,
right-click References and select System.ServiceModel from the list. You’ll also
need to add the following using statement to Service.cs:
using
System.ServiceModel;
To turn this interface into a service contract, you’ll need to explicitly decorate
the interface with the ServiceContractAttribute. In addition, each method
should be decorated with the OperationContractAttribute to include it in the
service contract. In this case, you’ll make IHelloIndigoService a service contract
and expose HelloIndigo( ) as its only service operation by applying these
attributes as shown here:
[ServiceContract(Namespace="http://www.thatindigogirl.com/samples/2006/06")]
public interface IHelloIndigoService
{
[OperationContract]
string HelloIndigo( );
}
Providing a namespace for the ServiceContractAttribute reduces the
possibility of naming collisions with other services. This will be
dicussed in greater detail in Chapter 2.
In the same file, create a service type to implement the service contract. You can
modify the existing class definition, renaming it to HelloIndigoService. Then
add the IHelloIndigoService interface to the derivation list and implement
HelloIndigo( ) with the following code:
public class HelloIndigoService : IHelloIndigoService
{
public string HelloIndigo( )
{
return "Hello Indigo";
}
}
Compile the service project.
At this point, you’ve created a service contract with a single operation and implemented
it on a service type. The service is complete at this point, but to consume it
from a client application, you will need to host it first.
Hosting a service
Next, add a new console application to the solution. This will be the host application.
You’ll instantiate a ServiceHost instance for the service type and configure a
single endpoint.
Go to the Solution Explorer and add a new Console Application project to the
solution. Name the new project Host.
Add a reference to the System.ServiceModel assembly, and add the following
using statement to Program.cs:
using System.ServiceModel;
You will be writing code to host the HelloIndigoService type. Before you can do
this, you must add a reference to the HelloIndigo project.
Create a ServiceHost instance and endpoint for the service. Open Program.cs in
the code window and modify the Main( ) entry point, adding the code shown in
Example 1-1. This code initializes a ServiceHost instance specifying the service
type and a base address where relative service endpoints can be located. It also
adds a single relative endpoint for the service. In this case, a base address is provided
for HTTP protocol, and the relative endpoint uses one of the standard
bindings, BasicHttpBinding, based on HTTP protocol.
Compile and run the host to verify that it works. From Solution Explorer, rightclick
on the Host project node and select "Set as Startup Project." Run the
project (F5), and you should see console output similar to that shown in
Figure 1-17.
Stop debugging and return to Visual Studio.
Example 1-1. Code to programmatically initialize the ServiceHost
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService),
new Uri("http://localhost:8000/HelloIndigo")))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),
new BasicHttpBinding( ), "HelloIndigoService");
host.Open( );
Console.WriteLine("Press to terminate the service host");
Console.ReadLine( );
}
}
You now have a host application for the service. When it is running, clients will be
able to communicate with the service. The next step is to create a client application.
Creating a proxy to invoke a service
Now you will create a new console application to test the service. To do this, the client
requires metadata from the service and information about its endpoint. This information
will be used to initialize a client proxy that can invoke service operations.
Go to Solution Explorer and add a new Console Application to the solution.
Name the new project Client.
As you might expect, this project also requires a reference to System.ServiceModel.
Add this reference and add the following using statement to Program.cs:
using System.ServiceModel;
Copy the service contract to the client. First, add a new class to the Client
project, naming the file ServiceProxy.cs. Open this new file in the code window
and add the IHelloIndigoService contract metadata as shown in Example 1-2.
This service contract supplies the necessary metadata to the client, describing
namespaces and service operation signatures.
Now you can add code to invoke the service endpoint. Open Program.cs and
modify the Main( ) entry point by adding the code as shown in Example 1-3. This
code uses the ChannelFactory to create a new channel to invoke the service. This
strongly typed channel reference acts as a proxy. The code also initializes an
EndpointAddress with the correct address and binding expected by the service
endpoint.
Test the client and service. Compile the solution and run the Host project first,
followed by the Client project. The Client console output should look similar to
that shown in Figure 1-18.
Example 1-2. Service contract metadata for the client
using System.ServiceModel;
[ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06")]
public interface IHelloIndigoService
{
[OperationContract]
string HelloIndigo( );
}
Example 1-3. Code to invoke a service through its generated proxy
static void Main(string[] args)
{
EndpointAddress ep = new
EndpointAddress("http://localhost:8000/HelloIndigo/HelloIndigoService");
IHelloIndigoService proxy = ChannelFactory.
CreateChannel(new BasicHttpBinding( ), ep);
string s = proxy.HelloIndigo( );
Console.WriteLine(s);
In the next few sections, I will explain in more detail the steps you completed and
the features you explored in the lab.
Assembly Allocation
The first thing I’d like to touch on is the allocation of assemblies when you create a
new solution that includes services. For example, in this lab you created a new solution
with three projects: one for the service, another for the host, and another for the
client. Note that the service definition is decoupled from the host project. This is an
approach I always recommend because it allows you to host the same service in multiple
environments. For example, you may need to expose a service behind the firewall
over TCP, and yet also allow remote, interoperable clients to consume it over
HTTP. These two approaches require distinct hosting environments (specifically, a
Windows service and IIS, as I will discuss in Chapter 4 at length). For simplicity,
many examples may couple service and host, but this is merely a convenience—not a
practical approach. As such, at a minimum I recommend that you always create a
separate project for service contracts and services.
Services are the window through which business functionality is invoked, but business
logic has no place in the service assembly. Business functionality should never
be coupled with the service implementation because it is possible that multiple services
and applications may need to reuse the same business logic. Furthermore, while
you may use services to reach that functionality in most cases, what if you needed to
expose an Enterprise Service component to interoperate with a particular application
or system? If business logic is stored in its own assemblies, this type of sharing is
made easy.
Another reason to decouple business logic from service implementation is to improve
manageability and versioning. The service tier may need to coordinate logging activities
and exception handling around calls to business components, and the service tier
may need to be versioned, while business components and associated functionality
have not changed. For this reason, I always recommend that business components,
data access components, and other dependencies of the business tier also represent a
separate set of assemblies in your solution. Figure 1-19 illustrates this breakdown
from a high level.
There may be times when it is desirable to share the service contracts with client
applications. In that case, service contracts and service implementations may also be
decoupled. This makes it possible to share the metadata of the service without sharing
the implementation.
Later in this chapter, you’ll see a scenario in which the service contract
and service are decoupled.
Defining a Service
The first step in creating a service is to define a service contract. You create a service
contract by applying the ServiceContractAttribute to an interface or type. Methods
on the interface or type will not be included in the service contract until the
OperationContractAttribute is applied. In a typical service contract, all methods will
be included in the contract—after all, the entire reason for defining a service contract
is to expose operations as part of a service. Business interfaces should not be
directly converted into service contracts. Likewise, business components should not
be directly converted to services. The service tier should instead be explicitly
defined with the sole purpose of exposing public functionality and should internally
consume business components, rather than embed business logic with the
service implementation.
When you implement a service contract as an interface, the service type implements
this interface. In this lab, the service implements a single service contract,
IHelloIndigoService. This contract exposes a single operation, HelloIndigo( ).
An alternative to this approach is to apply both the ServiceContractAttribute and
the OperationContractAttribute directly to the service type. Example 1-4 shows the
changes you would make to the lab to achieve this. Here is a summary of those
changes:
When you apply the ServiceContractAttribute to the service type, the service
type name becomes the official name of the service contract. Thus, this is the
name that must be provided when you create a new endpoint (see
AddServiceEndpoint( )).
On the client side, the service contract can still be represented as an interface (the
client only requires metadata) but the name of that interface (the service contract)
must match the new service contract name, HelloIndigoService—instead of
IHelloIndigoService. To update the lab, you can rename the interface at the client,
or specify a value for the Name property of the ServiceContactAttribute as
shown in Example 1-4. Service contracts are discussed in Chapter 2.
The following sample illustrates the coupling of service contracts
with service type: <YourLearningWCFPath>\Sample\ServiceContracts\ServiceContractOnServiceType.
Example 1-4. Changes that support defining the service contract with the service type
Any managed process can host services. Within that process, you can create one or
more ServiceHost instances, each associated with a particular service type and exposing
one or more endpoints for that type. This lab shows you how to host a service by
creating an instance of the ServiceHost type for the HelloIndigoService type within a
console application.
Before opening the ServiceHost instance, you can also provide it with base addresses
if you are planning to create relative endpoints. In order to reach the service, at least
one endpoint is required. To programmatically supply base addresses to the
ServiceHost, you can pass them to the constructor. The ServiceHost type also provides
an AddServiceEndpoint( ) method to create endpoints as shown here (from
Example 1-1):
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService),
new Uri("http://localhost:8000/HelloIndigo")))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),
new BasicHttpBinding( ), "HelloIndigoService");
// other code
}
In a simple scenario, the ServiceHost need only know its service type and associated
endpoints where the service can be reached. This information is used to create a
server channel that can receive and process messages. The channel is created when
you call the Open( ) method on the ServiceHost instance. This creates a channel listener
to receive messages for the service through its associated endpoints. The receiving
channel processes incoming messages, invokes service operations, and processes
responses. When the Close( ) method is called, the channel is gracefully disposed of
after processing any remaining requests. In Example 1-1, Close( ) is automatically
called when code block associated with the using statement ends.
The using statement can be applied to any type that implements
IDisposable. At the end of the using statement, Dispose( ) is called
within a try…finally block to ensure cleanup even in the case of an
exception.
Exposing Service Endpoints
Endpoints expose service functionality at a particular address. Each endpoint is associated
with a particular contract and a set of protocols as defined by the binding configuration.
For each service, one or more endpoints may be exposed if multiple
contracts are present or if multiple protocols are desired to access service functionality.
Figure 1-20 illustrates how the ServiceHost instance exposes endpoints to clients
and how the proxy invokes service operations at a particular endpoint.
As the lab illustrates, to create a service endpoint you provide an address, a binding,
and a contract.
Addresses
The address can be a complete URI or a relative address like that used in the lab. The
following shows you how to initialize an endpoint with a complete URI without supplying
a base address to the ServiceHost:
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService)))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),
new BasicHttpBinding( ), "http://localhost:8000/HelloIndigo/HelloIndigoService");
// other code
}
If you supply a relative address it is concatenated with the ServiceHost base address for
the matching protocol. The following illustrates providing an HTTP base address to
the ServiceHost constructor and providing a relative address to AddServiceEndpoint( ):
using (ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService),
new Uri("http://localhost:8000/HelloIndigo")))
{
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),
new BasicHttpBinding( ), "HelloIndigoService");
// other code
}
// Resulting endpoint address
http://localhost:8000/HelloIndigo/HelloIndigoService
In practice, a base address should be supplied for each transport protocol over which
the service can be accessed—for example, HTTP, TCP, named pipes, or MSMQ. In
the event an endpoint address includes a complete URI, the base address will be
ignored.
Using relative endpoint addressing makes is possible to modify the
base URI to move all associated relative endpoints to a new domain or
port. This can simplify the deployment process.
Bindings
The binding provided to an endpoint can be any of the standard bindings supplied by
the service model. In this example, a new instance of the standard BasicHttpBinding is
used to initialize the endpoint:
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService),
new BasicHttpBinding( ), "HelloIndigoService");
The choice of binding defines the communication channel. For an endpoint,
BasicHttpBinding, for example, supports requests over HTTP protocol sent in text
format without any additional protocols for addressing, reliable messaging, security,
or transactions.
In this chapter, you will employ other standard bindings, but you
should look to Chapter 3 for an in-depth discussion of bindings, channels,
and overall service model architecture.
Contracts
Each endpoint is associated with a particular service contract that determines the
operations available at the endpoint. Only one service contract exists in this lab, but
a service with multiple contracts could expose a different endpoint for each contract
it wants to make accessible to clients.
Creating a Client Proxy
Clients use a proxy to consume a service endpoint. A proxy can be created manually
using a channel factory, or it can be generated using tools. This lab explores the
former and shows you the bare necessities required to communicate with a service:
The address of the service endpoint
The protocols required to communicate with the service endpoint, or the binding
The service contract metadata as described by the service contract associated
with the endpoint
Essentially, the client proxy requires information about the service endpoint it wishes
to consume. In this lab, you learned how to manually create the proxy using
ChannelFactory<T>, as shown here:
EndpointAddress ep = new
EndpointAddress("http://localhost:8000/HelloIndigo/HelloIndigoService");
IHelloIndigoService proxy = ChannelFactory<IHelloIndigoService>.
CreateChannel(new BasicHttpBinding( ), ep);
ChannelFactory<T> is a service model type that can generate the client proxy and
underlying channel stack. You provide the address, binding, and service contract
type and call CreateChannel( ) to generate the channel stack discussed earlier. In this
lab, you made a copy of the service contract (not the implementation) in the client
application in order to supply it as the generic parameter type to ChannelFactory<T>.
The address and binding supplied matched those of the service. The result is that the
client proxy knows where to send messages, what protocols to use, and which operations
it can call.
In order for communication between client and service to succeed, the binding must
be equivalent to the binding specified for the service endpoint. Equivalence means
that the transport protocol is the same, the message-encoding format is the same,
and any additional messaging protocols used at the service to serialize messages are
also used at the client. This lab achieves this by applying the same standard binding,
BasicHttpBinding, at the client and service—thus, they are equivalent. Another
requirement for successful communication is that the service contract used to initialize
the proxy has equivalent operation signatures and namespace definitions. This is
achieved in this lab by making an exact copy of the service contract at the client.
You may be wondering: how can the client discover the correct
address, binding, and contract associated with a service endpoint? In
the next lab, you’ll learn how to generate client proxies and configuration
to consume a service without having access to the service code
base.
WinConnections Conference Fall 2008 Don’t miss the premier event for Microsoft IT Professionals in Las Vegas, November 10-13. Register and book your room by August 25 and receive a FREE room night (based on a three night minimum stay).
Master SharePoint with 3 eLearning Seminars Learn how to build a better SharePoint infrastructure and enable powerful collaboration with MVPs Dan Holme and Michael Noel. Register today!
SharePointConnections Conference Fall 2008 Don’t miss the premier event for Microsoft IT Professionals in Las Vegas, November 10-13. Register and book your room by August 25 and receive a FREE room night (based on a three night minimum stay).
VMworld 2008 - Sign Up Today! Join your peers on September 15-18 at The Venetian Hotel in Las Vegas as VMware hosts VMworld 2008, the leading Virtualization event.
Microsoft® Tech•Ed EMEA 2008 IT Professionals Advance your thinking with new ideas and practical real-world solutions at Microsoft’s FIVE day technical infrastructure conference 3-7 Nov., 2008. Register before 26 September 2008 to save €300.
Order Your Fundamentals CD Today! Gain an introduction to Exchange, learn server security requirements, and understand how unified communications can play a role in your messaging strategies with this free Exchange CD.
Are You Really Compliant with Software Regulations? View this web seminar that will help you with compliance best practices and check out a management solution to assure that you won’t be in jeopardy of an audit.
Virtualization Congress Oct. 14-16 in London Don't miss Virtualization Congress, the premiere EMEA conference dedicated to hardware, OS and application virtualization. Oct. 14-16 in London.