Services and the WCF

In this fourth part of a ten-part series focusing on the Windows Communication Foundation (WCF), aka "Indigo," you'll learn how to create a proxy to invoke a service, how to define a service, and more. This article is excerpted from chapter 1 of the book Learning WCF A Hands-on Guide, written by Michele Leroux Bustamante (O'Reilly, 2007; ISBN: 0596101627). Copyright © 2007 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 2
April 03, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

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 projectClient.
  2. As you might expect, this project also requires a reference toSystem.ServiceModel. Add this reference and add the followingusingstatement to Program.cs:

      using System.ServiceModel;
  3. Copy the service contract to the client. First, add a new class to theClientproject, naming the fileServiceProxy.cs. Open this new file in the code window and add theIHelloIndigoServicecontract 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 theMain()entry point by adding the code as shown in Example 1-3. This code uses theChannelFactoryto create a new channel to invoke the service. This strongly typed channel reference acts as a proxy. The code also initializes anEndpointAddresswith the correct address and binding expected by the service endpoint.
  5. Test the client and service. Compile the solution and run theHostproject first, followed by theClientproject. TheClient 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<IHelloIndigoService>.
CreateChannel(new BasicHttpBinding(), ep);
  string s = proxy.HelloIndigo();
  Console.WriteLine(s);
 
Console.WriteLine("Press <ENTER> to terminate Client.");
  Console.ReadLine();
}


Figure 1-18.  Output from the Client application

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.


Figure 1-19.  Assembly allocation for services, hosts, business components, and data access

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 theServiceContractAttributeand theOperationContractAttributedirectly 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:

  1. When you apply theServiceContractAttributeto 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 (seeAddServiceEndpoint()).
  2. 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 ofIHelloIndigoService. To update the lab, you can rename the interface at the client, or specify a value for theName property of theServiceContactAttributeas 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 HelloIndigoServicetype within a console application.

Before opening theServiceHostinstance, 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 theServiceHost, you can pass them to the constructor. TheServiceHosttype also provides anAddServiceEndpoint()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, theServiceHostneed 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 theOpen()method on theServiceHost 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 theClose()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 theusingstatement ends.

The using statement can be applied to any type that implementsIDisposable. At the end of the using statement,Dispose()is called within atry...finallyblock 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.


Figure 1-20.  ServiceHost exposes endpoints, and client proxies target a specific 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 theServiceHost base address for the matching protocol. The following illustrates providing an HTTP base address to theServiceHostconstructor and providing a relative address toAddServiceEndpoint():

  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 usingChannelFactory<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 callCreateChannel()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 toChannelFactory<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.

Generating a Service and Client Proxy

In the previous lab, you created a service and client from scratch without leveraging the tools available to WCF developers. Although this helps you to understand the raw requirements for sending messages between clients and services, in reality, developers need tools to be productive. This time around, I'll show you how to use several such tools that help you to generate services, access metadata, create configuration settings, and generate proxies. Specifically, you'll use the following:

  • Visual Studio service templates
  • Service Configuration Editor
  • ServiceModel Metadata Utility (SvcUtil)

The primary goal of the lab in this section will be to improve your productivity for building clients and services, but several other concepts will be discussed in the process. To begin with, you'll use declarative configuration settings instead of code to configure the service host and client. To enable proxy generation, you'll access service metadata, which involves enabling a service behavior. In addition, you'll learn more about service configuration settings for base addresses, endpoints, bindings and behaviors.

After you complete the lab, I'll spend some time discussing these concepts.

Please check back next week for the continuation of this article.

blog comments powered by Disqus
BRAINDUMP ARTICLES

- Microsoft Windows 8 Committed to Cloud Compu...
- Independent Developers Favor Windows Phone 7
- Dell Introduces VMware-based Cloud
- Microsoft and Skype Agree to Acquisition Deal
- Transfer Contacts in Microsoft Outlook
- Zune`s Next Steps
- Safari Books Online Review
- Does Microsoft Get Touch Screens Now?
- Microsoft`s Record Quarterly Earnings Not En...
- Basic Operations and Registers in Assembly
- Assembly Coding within Visual C/C++ IDE
- New Microsoft Office Coming with a Twist
- Microsoft`s FUSE Labs Unveils Spindex Social...
- HP Slate with Windows 7: Dead or Alive?
- Windows Phone 7 Mobile OS to Rival Android a...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 7 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials