Hello Indigo

  Windows IT Pro
Windows IT Library
  - Advertise        
Windows IT Pro Logo

  Home  |   Books  |   Chapters  |   Topics  |   Authors  |   Book Reviews  |   Whitepapers  |   About Us  |   Contact Us  |   ITTV  |   IT Jobs

search for  on    power search   help
 






Hello Indigo
View the book table of contents
Author: Michele L. Bustamante
Published: May 2007
Copyright: 2007
Publisher: O'Reilly Media
 


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:
net.tcp://localhost:9000
net.pipe://mymachinename
http://localhost:8000
http://www.anydomain.com
net.msmq://localhost
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:
net.tcp://localhost:9000/ServiceA
net.pipe://mymachinename/ServiceB
http://localhost:8000/Services/ServiceA
http://www.mydomain.com/ServiceA
net.msmq://localhost/QueuedServices/ServiceA
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.
  1. 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.


  2. 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.


  3. 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( );
    }
  4. 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;
  5. 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.

  6. 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";
        }
    }
  7. 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.
  1. Go to the Solution Explorer and add a new Console Application project to the solution. Name the new project Host.


  2. Add a reference to the System.ServiceModel assembly, and add the following using statement to Program.cs:
    using System.ServiceModel;
  3. You will be writing code to host the HelloIndigoService type. Before you can do this, you must add a reference to the HelloIndigo project.


  4. 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.


  5. 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.


  6. 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.
  1. Go to Solution Explorer and add a new Console Application to the solution. Name the new project Client.


  2. 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;
  3. 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.


  4. 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.


  5. 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
// HelloIndigo Project – Service.cs

[ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06")]
public class HelloIndigoService
{
  [OperationContract]
  public string HelloIndigo( )
  {
    return "Hello Indigo";
  }
}

// Host Project – Program.cs

host.AddServiceEndpoint(typeof(HelloIndigo.HelloIndigoService), new BasicHttpBinding( ),
"HelloIndigoService");

// Client Project – ServiceProxy.cs

[ServiceContract(Name="HelloIndigoService", Namespace = "http://www.thatindigogirl.com/
samples/2006/06")]
public interface IHelloIndigoService
{
    [OperationContract]
    string HelloIndigo( );
}

Hosting a Service

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.





Page: 1, 2, 3,

next page



ADS BY GOOGLE SPONSORED LINKS FEATURED LINKS

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).

Maximize your SharePoint Investment – 8 Cities
Discover best practices and tips for both architecting and administering SharePoint. Early Bird Price of $99 through Sept 15th.

Find a new job now on the all new IT Job Hound!
Search jobs, post your resume, and set up job e-mail alerts!

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!

Top Tools for Virtualization Disaster Recovery & Replication
View this web seminar on August 14th to learn about two tools that will result in faster backup and restore with P2V disaster recovery.

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.



When managing just VMware isn’t enough
Plan/Manage/Secure – NetIQ VMware management. Download whitepaper.

What’s up with your network? Find out with ipMonitor
Availability monitoring for servers, applications and networks – FREE trial

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.
Windows IT Pro Home Register FAQ for Windows WinInfo News
Europe Edition About Us Contact Us/Customer Service Media Kit Affiliates / Licensing  
SQL Server Magazine Office & SharePoint Pro Windows Dev Pro IT Job Hound ITTV
IT Library Technical Resources Directory Connected Home Windows Excavator Windows SuperSite 
 
 Windows IT Pro is a Division of Penton Media Inc.
 Copyright © 2008 Penton Media, Inc., All rights reserved. Terms and Use | Privacy Statement | Reprints and Licensing