Handling Multiple Contracts with Indigo

In this conclusion to a ten-part series on the Windows Communication Foundation (WCF), aka Indigo, you'll learn about the intricacies of multiple service contracts. 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 / 1
May 15, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Implementing Multiple Contracts

Designing service contracts is not as simple as just exposing existing business components as services. In all likelihood, services will aggregate calls to many logically related business components. This requires forethought into the use cases through each service. Irrespective of this aggregation and design effort, it is still possible that the functionality exposed by a single service should not be lumped into one big service contract. Here are some cases in which multiple contracts could exist on a single service:

  1. To separate logically related operations for different features
  2. To separate queued operations from non-queued operations
  3. To provide a different entry point for external and internal consumers of the service

If your service contracts are implemented on CLR interfaces (as I've recommended) then implementing multiple contracts on a service is as simple as implementing multiple interfaces. In this lab, each service implements a main service contract (IServiceA andIServiceB, respectively) and an administrative contract (IAdmin).

Contracts can facilitate the logical separation of functionality exposed by a service. For example, in the lab the main service contract for each service holds core business operations for the service. The administrative contract provides a consistent set of administrative functions that any service can expose. Both services implement the same administrative contract, which means both services expose a consistent set of operations, although the internal implementation may be quite different. Because of the presumed sensitivity of administrative operations, the lab exposes these operations over TCP or named pipes, which implies access behind the firewall. To enable internal applications and business partners to access the core service functionality, the main service contracts are exposed over two endpoints: one for TCP or named pipes, the other for Internet access over HTTP.

Hosting Multiple Services

A ServiceHost instance is required for each service type in order to expose endpoints to calling clients. When you host in IIS or WAS, a .svc endpoint is supplied for each service type, with a @ServiceHost directive that links the .svc endpoint to the actual service type. Thus, if you have multiple services to host in IIS, you provide a .svc for each to support message-based activation and configure the service model section as you would for any service type.

In self-hosting environments, you are responsible for initializing eachServiceHostinstance. You can initialize aServiceHostfor each service type as shown here (the complete code listing is shown in Example 1-20 ):

  ServiceHost hostA = new ServiceHost(typeof(BusinessServices.ServiceA));
  ServiceHost hostB = new ServiceHost(typeof(BusinessServices.ServiceB));

  hostA.Open();
  hostB.Open();

EachServiceHostis initialized with its own base addresses and service endpoints according to the<service>section with the matching type (see Example 1-19). EachServiceHostcan also be programmatically initialized using the techniques illustrated earlier in this chapter.

Proxy Generation for Multiple Contracts and Endpoints

Adding a service reference generates the proxy and configuration settings necessary to access a particular service. If the service implements multiple contracts, a proxy type is generated for each contract. For example, in this lab when you add a service reference to ServiceA in the ExternalClient project, the following proxies are generated--one for IServiceA, another for IAdmin:

  public partial class ServiceAClient :
 
System.ServiceModel.ClientBase <ExternalClient.ServiceA.IServiceA>,
 
ExternalClient.ServiceA.IServiceA

  public partial class AdminClient :
  System.ServiceModel.ClientBase <ExternalClient.ServiceA.IAdmin>,
 
ExternalClient.ServiceA.IAdmin

Likewise, when you add a service reference toServiceB a proxy is generated for both contracts:IServiceBandIAdmin.

In theory, because theIAdminservice contract is the same for both services, they could share a proxy, but SvcUtil generates proxy types for all contracts and has no knowledge of the code you have already generated.

In addition to generating proxies, SvcUtil generates the configuration necessary for each endpoint exposed by each service. SvcUtil always provides a name for each<endpoint>element, so you can specify the correct endpoint to use by name when constructing each proxy. Example 1-27 shows the client endpoints generated forServiceAandServiceB; the endpoints used in the lab for theExternalClient are shown in bold.

Example 1-27. Client endpoints generated for ServiceA and ServiceB

<client>
 
<endpoint address="net.tcp://localhost:9000/Admin" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IAdmin" contract="ExternalClient.ServiceA.IAdmin" name="NetTcpBinding_IAdmin" />
 
<endpoint address="http://localhost:8000/ServiceA" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IServiceA" contract="ExternalClient.ServiceA.IServiceA" name="BasicHttpBinding_IServiceA" />
  <endpoint address="net.tcp://localhost:9000/ServiceA" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServiceA"
contract="ExternalClient.ServiceA.IServiceA"
name="NetTcpBinding_IServiceA" />
 
<endpoint address="net.pipe://localhost/Admin" binding="netNamedPipeBinding"
bindingConfiguration="NetNamedPipeBinding_IAdmin" contract="ExternalClient.ServiceB.IAdmin" name="NetNamedPipeBinding_IAdmin" />
 
<endpoint address="http://localhost:8001/ServiceB" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IServiceB" contract="ExternalClient.ServiceB.IServiceB" name="BasicHttpBinding_IServiceB" />
  <endpoint address="net.pipe://localhost/ServiceB" binding="netNamedPipeBinding" bindingConfiguration="NetNamedPipeBinding_IServiceB" contract="ExternalClient.ServiceB.IServiceB" name="NetNamedPipeBinding_IServiceB" /> </client>

Although the client may not have network rights to invoke the TCP or named pipe endpoints, these endpoints are still part of the service description (WSDL) and therefore are visible to the client.

Recall that a WSDL document is created for each service type. Thus, to prevent remote clients from seeing internal endpoints that they should not access, you can create different service types for internal and external use--funneling them to the same implementation code. On external service types, you can expose endpoints only for supported contracts over HTTP. For internal service types, you can expose internal contracts and TCP and named pipes endpoints. To modify the lab in support of this scenario, you might see the following service types:

  public class ServiceA : IServiceA {...}
  public class InternalServiceA : IServiceA, IAdmin {...}
 
public class ServiceB : IServiceB {...}
  public class InternalServiceB : IServiceB, IAdmin {...}

In the host configuration, each service type would be defined in a separate<service>element, exposing only the required endpoints. A compressed view of the required<service>elements is shown here:

  <service name="BusinessServices.ServiceA" ...> 
  <service name="BusinessServices.InternalServiceA" ...>
  <service name="BusinessServices.ServiceB" ...>
  <service name="BusinessServices.InternalServiceB" ...>

The following sample illustrates this scenario: <YourLearningWCFPath>\Samples\ServiceContracts\ MultiContractServices_UniqueServiceTypes.

Proxy Initialization and Lifetime

Each client proxy opens a communication channel to invoke a service endpoint. The proxy can be programmatically initialized in code or declaratively initialized per the client's service model configuration. If there is only one endpoint configured for a particular service contract, there is no need to specify an endpoint configuration name to the constructor of the channel factory or proxy.

When you useChannelFactory<T>to create the channel from the default endpoint, you pass empty quotes to the constructor. This example expects that only one endpoint is configured forIServiceA:

  ChannelFactory<IServiceA> factoryA = new
  ChannelFactory<IServiceA>("");
  m_proxyA = factoryA.CreateChannel();

Generated proxies provide a default constructor to achieve the same result:

  m_proxyA = new ExternalClient.ServiceA.ServiceAClient();

On the other hand, when multiple endpoints exist for the same contract, you must provide a configuration name as shown here forChannelFactory<T>and for a generated proxy:

  ChannelFactory<BusinessServiceContracts. IServiceA> factoryA = new 
  ChannelFactory<BusinessServiceContracts. IServiceA>("BasicHttpBinding_IServiceA");
  proxyA = factoryA.CreateChannel();

  m_proxyA = new
  ExternalClient.ServiceA.ServiceAClient("BasicHttpBinding_IServiceA");

In either case, the lifetime of the communication channel is controlled by the proxy reference. If the client application intends to invoke the service endpoint repeatedly, it is better not to recreate the proxy each time an operation is invoked. Instead, the proxy should be scoped to the lifetime of the application.

When the application is shutting down, you should close the proxy to speed up the release of resources. When you are working with a channel factory to create the proxy reference, you must cast toICommunicationObjectin order to call itsClose()method (see Example1-24). The equivalent inline steps would be as follows:

  ICommunicationObject proxyACommunication = m_proxyA as ICommunicationObject;
  ...
  proxyACommunication.Close();

This step is required because the channel factory returns a reference to the service contract, which doesn't expose aClose()method. Still, the underlying object is aCommunicationObjectthat implementsICommunicationObject.

Proxies generated with SvcUtil include code to wrap the inner communication channel. In addition, each generated proxy type implementsICommunicationObjectdirectly, and thus provides aClose()method.

Be aware that the channel stack beneath the proxy reference can be put into a faulted or invalid state. For example, if the service is no longer available, or if the service throws an exception, or if a timeout occurs at either end. In Chapter 8, Ill discuss exception handling.

Another point to note is that the lifetime of the communication channel should not be confused with the lifetime of the service instance instantiated by the host to handle a request. In fact, a different service instance may be allocated for every call even if the client uses the same channel. This behavior is controlled by the service. Service instancing and throttling behaviors are covered in Chapter 5.

Sharing Service Contracts

This lab illustrates an alternate approach for sharing metadata with the client. Instead of generating a proxy using SvcUtil, a class library containing only service contracts is shared by the service library and the internal client application. This approach is useful in an environment where you own both sides: client and service. This approach can simplify steps in development, help you avoid the internal complexity of types generated by SvcUtil, and even allow you to exercise more control over service contract versioning on both ends.

Realistically, remote clients such as Internet clients may not be owned, which is why the more traditional approach of sharing contracts via add service reference is used.

Duplicating Operations

This lab illustrates exposing two different contracts on each service. These contracts each have unique operations: a set for the business functionality exposed by the service, and a set for administrative functions. You may also want to expose a subset of business operations for external clients while exposing the complete set of business functionality to internal clients.

To achieve this, you could create internal and external interfaces for the service contract, for example:IServiceAandIInternalServiceA. The external interface,IServiceA, would in this case contain a subset of the functionality exposed byIInternalServiceA(see Example 1-28).

Example 1-28. Internal and external service contracts with duplicate operations

[ServiceContract(Namespace = http://www.thatindigogirl.com/samples/2006/06)] public interface IServiceA
{
 
[OperationContract]
 
string Operation1();
 
[OperationContract]
 
string Operation2();
}

[ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06")] public interface IInternalServiceA
{
 
[OperationContract]
 
string Operation1();
 
[OperationContract]
 
string Operation2();
 
[OperationContract]
 
string Operation3();
}

If you expose each of these contracts on their own service types (ServiceAandInternalServiceA, respectively), external clients will never see the functionality exposed to internal clients because they work from a different WSDL document. However, the implementation can still be the same for each service implementation.

The following sample illustrates the scenario:<YourLearningWCFPath>\ ServiceContracts\ Samples\ InternalExternalServiceTypes.sln.

Summary

This chapter covered a lot of ground, beginning with a look at the purpose of WCF, the problems it solves and its alignment with SOA, through discussion of the fundamentals developers should know before they begin working with WCF. I also touched on the overall architecture of WCF, though this is covered in greater detail in Chapter 3. Through hands-on practice and discussion you should be comfortable now with the following concepts:

  1. Defining service contracts and services
  2. Hosting services in a console application or in IIS
  3. Exposing endpoints for a service using various standard bindings
  4. Working with Visual Studio templates and WCF tools to improve productivity
  5. Working with service metadata and configuring related service behaviors
  6. Generating proxies to invoke services

Of course, the next step is to start diving into the details of service contracts, bindings, and hosting. In addition, you'll need to learn more about service behaviors and messaging protocols that handle instancing, throttling, reliability, security and exception handling.

Since service contracts are central to defining the messages exchanged between clients and services, the next chapter will focus on this subject. In Chapter 2, I'll explain how to approach service contract design, how to work with complex types and how to control serialization on many levels. In the process of reading Chapter 2, you'll further solidify your knowledge of the fundamental concepts touched on in this chapter.  

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 6 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials