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.

Lab: Using Tools to Generate Clients and Services

In this lab, you will generate service code using two approaches: by adding a service to an existing host and by generating a new service library, both using Visual Studio templates. To configure service endpoint for the host, this time you’ll use the Service Configuration Editor. To generate client proxies and related configuration you’ll use the ServiceModel Metadata Utility (SvcUtil). Both of these tools are available through Visual Studio.

Tip

Visual Studio 2008 includes productivity tools for WCF that are only in CTP format for Visual Studio 2005.

Using the WCF Service template

In this first section of the lab, you’ll create a new service using the WCF Service template and add it to an existing project. This template will add a sample service contract and service type to the project, along with the required service model assembly references.

  1. Start by opening an existing Visual Studio solution that contains two projects: a shell console client and host. The solution is located at <YourLearningWCFPath>\Labs\Chapter1\HelloIndigo\HelloIndigo.sln.

  2. First, you will add a new service to the host project. From Solution Explorer, right-click on the Host project node and select Add → New Item. Select the WCF Service template and name the file HelloIndigoService.cs. Two files are generated: HelloIndigoService.cs and IHelloIndigoService.cs.

  3. Open IHelloIndigoService.cs in the code window and add a namespace qualifier for the service contract, then modify the service operation name and signature to match the following code in bold:

    [ServiceContract(Namespace="http://www.thatindigogirl.com/samples/2006/06")]
    public interface IHelloIndigoService
    {
      [OperationContract]
      string HelloIndigo();
    }
  4. Open HelloIndigoService.cs and modify the service implementation to implement the correct operation signature. This is how the resulting service type should look:

    public class HelloIndigoService : IHelloIndigoService
    {
      public string HelloIndigo()
      {
        return "Hello Indigo";
      }
    }
  5. Open Program.cs and add code to the new Main() entry point so that it looks as follows:

    using System.ServiceModel;
    static void Main(string[] args)
    {
      using (ServiceHost host = new ServiceHost(typeof(HelloIndigoService)))
      {
        host.Open();
        Console.WriteLine(“Press <ENTER> to terminate the host application”);
        Console.ReadLine();
      {
    }
    }
  6. Compile the Host project.

At this point, you have defined a service inside the Host project and added code to host the service, but the implementation is incomplete. The ServiceHost requires at least one endpoint before clients can invoke the service.

Configuring service endpoints using the Service Configuration Editor

In this section, you will provide an endpoint for the ServiceHost—this time using the Service Configuration Editor. Unlike in the previous lab, endpoints will be configured using an external application configuration file. As such, you’ll open the application configuration file for the Host application using the tool to configure ServiceHost endpoints in that file.

  1. From Solution Explorer, go to the Host project and open the app.config file. A sample configuration has been created by the WCF Service template added in the previous section – but you will create a similar configuration from scratch. Delete this configuration so that the configuration file contains only the following:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    </configuration>
  2. From Solution Explorer right-click on the app.config file. Select Edit WCF Configuration. You’ll see the Service Configuration Editor interface shown in Figure 1-21.

    Tip

    If the context menu Edit WCF Configuration does not appear you may have to open the Service Configuration Editor once from the main menu. Select Tools → WCF Service Configuration Editor to launch the editor, close the dialog and retry the context menu.

    Go to the Tasks pane and click “Create a New Service”; the New Service Element Wizard will be displayed.

    Follow these instructions as you go through each page in the wizard:

    1. On the first page, you are asked to provide the service type. Browse to <YourLearningWCFPath>\Labs\Chapter1\HelloIndigo\Host\Bin\Debug and select Host.exe. The Type Browser dialog (shown in Figure 1-22) will list Host.HelloIndigoService as the only service available in the assembly. Select it from the list and click Open. Click Next to continue.

    2. Now you will be asked to specify a service contract. There is only one service contract implemented by HelloIndigoService, so the selected service contract should be Host.IHelloIndigoService. Click Next to continue.

    3. Select HTTP as your service communication mode and click Next.

    4. Select Basic Web Services interoperability as your interoperability method and click Next.

    5. Now you’ll be asked to provide an endpoint address. Here you can provide a relative address by clearing the current text and typing “HelloIndigoService.” Click Next.

    6. Review the configuration you have chosen for the service, then click Finish.

    Service Configuration Editor for WCF

    Figure 1-21. Service Configuration Editor for WCF

    The service type browser lists all service types in a particular assembly

    Figure 1-22. The service type browser lists all service types in a particular assembly

  3. Go to the Configuration pane in the Service Configuration Editor interface and expand the Endpoints folder beneath Host.HelloIndigoService. Select the only endpoint labeled (Empty Name).

    Go to the Service Endpoint pane, and in the General tab, provide the name basicHttp as shown in Figure 1-23. At this point, you have created a single, relative service endpoint.

    Configuring a service endpoint using the Service Configuration Editor

    Figure 1-23. Configuring a service endpoint using the Service Configuration Editor

  4. In this lab, the client will generate a proxy using SvcUtil. To support this, you’ll enable the metadata exchange behavior by adding a behavior to the service configuration.

    Go to the Configuration section and expand the Advanced folder. Select Service Behaviors and go to the Tasks pane to select New Service Behavior Configuration.

    Go to the Behavior pane and set the configuration name to serviceBehavior. Click the Add button to add a behavior element and select serviceMetadata from the list provided.

    Go to the Configuration pane and you’ll now see a new serviceMetadata node beneath serviceBehavior. Select the node and review the default settings in the General tab.

  5. The behavior must be explicitly associated to the service. Go to the Configuration pane and select the service node, Host.HelloIndigoService. Go to the Service pane and set the BehaviorConfiguration property to serviceBehavior (you can select it from the dropdown list).

  6. Enabling the metadata behavior is a good start, but a new endpoint is also required to support metadata exchange. Go to the Configuration pane, right-click on the Endpoints folder, and select New Service Endpoint.

    Go to the Service Endpoint pane and set the name to mex. In the Endpoint Properties section, set the Binding to mexHttpBinding. For the Contract property type, IMetadataExchange.

  7. In order to support metadata exchange, the host must have a base address for the metadata exchange protocol being used. In addition, since you supplied a relative address for the service endpoint, it also requires a base address matching the binding protocol. In this case, an HTTP base address will be used.

    Go to the Configuration pane and select the Host node beneath Host.HelloIndigoService. Go to the Host pane and select New to create a new base address. From the Base Address Editor, supply the following base address: http://localhost:8000/HelloIndigo.

  8. Save the configuration settings you just generated. Select File → Save followed by File → Exit. Return to Visual Studio and open the app.config for the Host project. You will see a <system.serviceModel> section like the one shown in Example 1-5.

  9. Compile the Host project and then run it without debugging. Leave the host running for the next step.

    Tip

    In Visual Studio 2008, you cannot add a service reference to a client application while the service is running in debug mode within the same Visual Studio instance. If you run the host project without debugging you will be able to add a service reference.

Example 1-5. Service model configuration generated by Service Configuration Editor

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="serviceBehavior">
        <serviceMetadata />
      </behavior>
    </serviceBehaviors>
  </behaviors>

  <services>
    <service behaviorConfiguration="serviceBehavior"
name="Host.HelloIndigoService" >
      <endpoint address="HelloIndigoService" binding="basicHttpBinding"
name="basicHttp" contract="Host.IHelloIndigoService" />
      <endpoint binding="mexHttpBinding" name="mex" contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8000/HelloIndigo" />
        </baseAddresses>
      </host>
    </service>
  </services>
</system.serviceModel>

You just created a declarative configuration for the ServiceHost, instead of programmatically initializing its base addresses and service endpoints. In addition, you enabled the service metadata behavior and created a metadata exchange endpoint so that clients can generate a proxy using SvcUtil. That’s the next step.

Generating a proxy with Add Service Reference

It’s time to generate code for the client to consume the service, starting by generating a client proxy. To achieve this you will use the Add Service Reference functionality exposed by Visual Studio, which uses the ServiceModel Metadata Utility (SvcUtil) to generate a proxy and configuration settings for that proxy.

  1. Go to the Client project and from Solution Explorer, right-click on the Client project node and select Add Service Reference. The dialog presented requires you to provide a valid base address to the service. Supply the base address http://localhost:8000/HelloIndigo and set the Service namespace to localhost. When you click OK to close this dialog, a service proxy and configuration file will be generated for the client application.

    To see the proxy, go to the Client project and expand the Service References folder. Beneath it you will see Reference.svcmap, and beneath that Reference.cs—the latter of which contains the proxy.

    A new configuration file, app.config, was also added to the project. This contains the service model configuration for the proxy. Later I’ll talk about how these things come together.

  2. Add code to the client application to invoke the service using the generated proxy. Go to the Client project and open Program.cs. Add code to the Main() entry point as shown in bold in Example 1-6.

    Compile the solution and run the Client project. The client’s console output should show the result of invoking the service’s HelloIndigo operation. After testing, stop debugging the Client project and close the Host console.

    Tip

    Most labs and sample code from this book use the same port numbers for HTTP and TCP respectively. Thus, if you forget to close down a host that is using a particular port number, and try to run a different host that uses the same, you will see an error since the port is already in use.

Example 1-6. Using a generated proxy to invoke a service

static void Main(string[] args)
{localhost.HelloIndigoServiceClient proxy = new
Client.localhost.HelloIndigoServiceClient()
  string s = proxy.HelloIndigo();
  Console.WriteLine(s);

  Console.WriteLine("Press <ENTER> to terminate Client.");
  Console.ReadLine();
}

This concludes one technique for generating a service, ServiceHost configuration, and a client proxy.

Creating a WCF Service Library

In this section, you will generate a service using another technique: adding a new class library that includes a WCF service. The WCF Service Library template is a quick and easy way to generate a new class library with a sample service contract, service type, and the appropriate assembly references.

  1. Go to the Solution Explorer and right-click on the solution node. Select Add → New Project and select the WCF Service Library template. Name the project HelloIndigo.

  2. Rename IService1.cs and Service1.cs to IHelloIndigoService.cs and HelloIndigoServices.cs, respectively.

  3. Modify the service contract that is supplied by the project template. Open IHelloIndigoService.cs in the code window and provide a namespace for the ServiceContractAttribute and change the interface definition to look as follows:

    [ServiceContract(Namespace="http://www.thatindigogirl.com/samples/2006/06")]
    public interface IHelloIndigoService
    {
      [OperationContract]
      string HelloIndigo();
    }
  4. Now, modify the service implementation so that it implements the new contract. Open HelloIndigoServices.cs, rename the service to HelloIndigoService and implement IHelloIndigoService as shown here:

    public class HelloIndigoService: IHelloIndigoService
    {
      public string HelloIndigo()
      {
        return "Hello Indigo";
      }
    }

    Tip

    The default data contract created when you used the WCF Service template is not necessary for this lab.

  5. Delete the app.config file created for the HelloIndigo project, and then compile the project.

    Tip

    The release of Visual Studio 2008 and .NET Framework 3.5 introduced a WCF Service Host and a WCF Test Client to simplify testing WCF services. When you create a new WCF Service Library the The app.config file is used to configure the WCF test host. I’ll briefly discuss these test tools in a later section.

  6. Now you will modify the existing host project so that it hosts this new service. From Solution Explorer, go to the Host project and select the files IHelloIndigoService.cs and HelloIndigoService.cs. Right-click on the selection and select Exclude From Project to avoid collision with the HelloIndigo library you’re about to reference.

  7. Add a reference to the HelloIndigo class library project. Right-click on the Host node and select Add Reference. From the Projects tab, select the HelloIndigo project.

  8. The ServiceHost must be modified to refer to the service type from this project. In the Program.cs file, modify the ServiceHost constructor to use the fully qualified name of the service, HelloIndigo.HelloIndigoService, as shown here:

    host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService));
  9. You’ll also have to edit the service model section of the configuration file to use the correct service and contract types. Go to the Host project and open the app.config file. Change the service type and contract type for the <service> configuration section as shown here in bold:

    <service behaviorConfiguration="serviceBehavior" name="HelloIndigo.
    HelloIndigoService">
      <endpoint address="HelloIndigoService" binding="basicHttpBinding"
    name="basicHttp" contract="HelloIndigo.IHelloIndigoService" />
    
      <!— other settings —>
    
    </service>
  10. Test the solution again by compiling and running the Host and then the Client.

Now you have learned how to create a new class library with a sample WCF service and seen the changes required to the service model configuration and ServiceHost to reference a different service type.

Generating a proxy using the Service Model Metadata Utility

In this section, you will generate a client proxy using the SvcUtil directly instead of using Add Service Reference. The purpose of this exercise is to show you how to exercise greater control over the generation of proxies and configuration settings.

  1. First, run the Host project without debugging so that the endpoint is available to generate a proxy.

    From the Windows Start menu, find the Microsoft Visual Studio 2008 program group and launch the Visual Studio 2008 Command Prompt beneath the Visual Studio Tools folder in the program group. Run the following command to generate a new proxy for the client application and replace the application configuration settings generated previously:

    svcutil /d:<YourLearningWCFPath>\Labs\Chapter1\HelloIndigo\Client /o:
    serviceproxy.cs /config:app.config http://localhost:8000/HelloIndigo

    The output should look similar to Figure 1-24.

    Warning

    The /d: option for SvcUtil allows you to provide a path where output files will be generated. In the Preface, I explained that I would be using the term <YourLearningWCFPath> to refer to your base path—where you unzipped the file provided with the book. Thus, if your base path is c:\LearningWCF, then your SvcUtil command in this example would be:

    svcutil/d:c:\LearningWCF\Labs\Chapter1\HelloIndigo\Client
     /o:serviceproxy.cs/config:app.config http://localhost:8000/
    HelloIndigo

    If your path includes spaces, such as c:\Learning WCF, then you will have to provide quotes to the path, as shown here:

    svcutil/d:"c\Learning WCF\Labs\Chapter1\HelloIndigo\Client"
     /o:serviceproxy.cs/config:app.config http://locahost:8000/HelloIndigo
  2. To use this proxy you’ll have to modify the client application. Go to the Client project. If you select the “Show all files” icon in Solution Explorer, you’ll see a new file beneath the project node. Right-click serviceproxy.cs and select “Include in Project.” Right-click Reference.svcmap beneath Service References\ localhost and select “Exclude from Project.”

    Now open Program.cs and modify the code that constructs the service proxy. The proxy that was generated does not belong to a namespace, so you must remove the fully qualified name for HelloIndigoServiceClient. The resulting code is:

    using (HelloIndigoServiceClient proxy = new HelloIndigoServiceClient())
  3. Compile and run the Client and test the solution once again.

Now let’s examine some of the ideas introduced in this lab in greater detail.

Output generated when SvcUtil generates a service proxy and configuration settings for the client

Figure 1-24. Output generated when SvcUtil generates a service proxy and configuration settings for the client

Service Templates

This lab introduces several Visual Studio templates for generating service code. In an existing project—no matter whether it is a class library, console application, Windows application, or web site—you can add a new WCF Service. This template generates a sample service contract and service type to get you started and adds the necessary service model assembly references. It also includes a smaple data contract (to be discussed in Chapter 2. When you want to create a separate class library for your services, to isolate it from the host project (my personal preference) you can either create a Class Library project and add a WCF Service, or create a WCF Service Library project. The latter template creates a new class library, including a sample service contract and service type, in addition to adding the service model assembly references. The WCF Service Library template also makes use of the new WCF Service Host and WCF Test Client tools introduced with Visual Studio 2008 (to be discussed). To host services belonging to a class library project you can either use the WCF Service Host or create a separate host project for the service. Although the WCF Service Host is convenient for simple tests, this lab illustrates the latter which gives you greater control over ServiceHost initialization—a practical necessity in most development environments.

Service templates for web sites are slightly different from other projects because the hosting environment is slightly different. The overall result is the same—sample code is generated and assembly references are added. The difference is that an additional file is generated with a .svc extension for web hosting. You’ll see this in the next lab.

Service Testing Tools

Visual Studio 2008 introduced two tools for testing WCF services:

  • WCF Service Host (WcfSvcHost.exe)

  • WCF Test Client (Wcf TestClient.exe)

The WCF Service Host executable provides a host process for the WCF services in a specific assembly, using the configuration settings provided by a specific application configuration file. One example of the command line to launch the test host is:

wcfsvchost /service:pathtoserviceassembly /config:pathtoserviceconfig

When you create a new project using the WCF Service Library template, the project is configured so that you can launch the resulting class library in debug mode (F5) and host all services in the assembly, using the configuration supplied by the app.config file created in the project. In addition, the project settings launch the WCF Test Client so that you can also test the services in the project without creating a test client application.

The WCF Test Client executable provides a dynamically generated user interface to test services described by a specific metadata endpoint. Thus, the command line instruction to do this is:

wcftestclient metadataexchangeaddress

These tools are useful for very simple and rapid testing scenarios however in most development environments it is necessary to generate richer test scenarios that include configurations for both client and service host that will be applicable in production. In fact, I recommend you create your WCF service libraries using the standard Class Library template rather than the WCF Service Library template to avoid cluttering your projects with configuration files and project settings that will not be useful in production.

Tip

For more information about the test tools visit the following links: http://msdn.microsoft.com/en-us/library/bb552363.aspx and http://msdn.microsoft.com/en-us/library/bb552364.aspx.

ServiceModel Metadata Utility

The ServiceModel Metadata Utility is a command-line tool that is installed with the Windows SDK for .NET 3.0—an executable file named svcutil.exe. This tool can be used for two key purposes:

  • To generate code and configuration settings from metadata

  • To generate metadata from code and configuration

With SvcUtil you can export and import metadata, validate services, and manipulate how types are generated and shared between services and clients. Add Service Reference uses SvcUtil to generate proxies and configuration, but from the command line, you can exercise greater control over this process.

This lab illustrates a very simple command-line instruction for generating a proxy and configuration file from a metadata exchange endpoint as follows:

svcutil /d:<YourLearningWCFPath>\Labs\Chapter1\HelloIndigo\Client /o:serviceproxy.cs
/config:app.config http://localhost:8000/HelloIndigo

You can also suppress the generation of configuration output with the following instruction:

svcutil /d:<YourLearningWCFPath>\Labs\Chapter1\HelloIndigo\Client /o:serviceproxy.cs/config:noconfig http://localhost:8000/HelloIndigo

To see all the options for SvcUtil, from the command line you can type:

svcutil /?

Using Add Service Reference to generate the client configuration and proxy will work for many cases. As of Visual Studio 2008 the Add Service Reference dialog supplies advanced options to control things such as how arrays are serialized, generating a proxy that can make asynchronous calls, and generating a proxy that shares assembly types with the service. Using the command line utility directly is still useful for automating how proxies and service contracts generated, and for things such as controlling the choice of WCF serializer the proxy should use. I’ll explore other uses for SvcUtil throughout this book.

Service Configuration Editor

The Service Configuration Editor is another tool that is available through Visual Studio 2008—an executable file named svcconfigeditor.exe. This tool is a wizard that helps you configure service model settings for WCF clients and services. You can launch the Service Configuration Editor directly from Solution Explorer to edit the <system.serviceModel> section for any client or host application configuration file (as the lab illustrates). The wizard guides you through steps to configure services, bindings, behaviors, and more, which is particularly useful when you are new to WCF since you may not be familiar with each section of the configuration file.

The service model configuration for clients and service are both encapsulated within the <system.serviceModel> configuration section so you can use this tool to edit both sides. When starting from scratch, you can use the tool to add new services or client endpoints, to attach behaviors to services or endpoints, to supply base addresses for the host, to supply metadata exchange endpoints, and even to customize binding configurations (something I’ll talk about in Chapter 3). For existing applications, you may just use the tool to view settings and make minor changes.

As I show you configuration settings throughout this book, I’ll talk about many settings in the <system.serviceModel> section—all of which can be configured using the Service Configuration Editor. But you’ll find that as you gain more experience with <system.serviceModel> settings, it is much more productive to edit the configuration file directly, relying on Intellisense.

ServiceModel Configuration

This lab illustrates the use of declarative configuration settings to configure the ServiceHost and the client proxy—although, in the latter case, you generated the configuration. Both ServiceHost and proxy types rely on programmatic or declarative configuration to initialize endpoints and configure behaviors. The latter technique provides greater deployment flexibility while programmatic settings enable you to enforce certain settings. In this section I’ll focus on the service model configuration settings.

All configuration settings related to the service model are contained within the <system.serviceModel> section new to WCF. Any application configuration file can contain this section, which means app.config for executables, and web.config for web sites. The service model is vast and there are many configuration options, most of which will be explored throughout the book. However, the core elements of this configuration you will repeatedly use are: <services>, <client>, <bindings>, <behaviors>. Table 1-7 provides a brief explanation of each section.

Table 1-7. Summary of core <system.ServiceModel> sections

Section

Description

<services>

This element contains one or more <service> definitions. Each <service> section is associated with a particular service type and includes any base addresses and endpoints to be exposed for that type.

<client>

This element contains one or more <endpoint> definitions for each service endpoint the client may consume. Each individual <endpoint> includes an address, binding, and contract matching a service endpoint. Client proxies select an endpoint to configure the client communication channel.

<bindings>

This element contains one or more binding configuration sections. This makes it possible to customize a binding instead of using the defaults provided by a standard binding, such as BasicHttpBinding. I’ll explore bindings in Chapter 3.

<behaviors>

This element contains <serviceBehaviors> and <endpointBehaviors>. At the host, the <service> configuration section may reference an element from the <serviceBehaviors> section to apply local behavioral settings to all endpoints for the service. Service endpoints can reference <endpointBehaviors> that apply only to that specific endpoint for the service. At the client, endpoints defined in the <client> section may reference an element from the <endpointBehaviors> section to apply local behavioral settings to the client communication channel.

Service model configuration settings can also be set at runtime through the proxy or ServiceHost; however, declarative configuration is often preferred to hardcoding settings in code. You can modify configuration files without impacting the compiled service or client code and this supports more flexible deployment scenarios.

Tip

Throughout this book, you will see examples that configure the service model in code where there are practical applications for it.

ServiceHost Initialization

The first lab illustrates how to configure the ServiceHost programmatically. This lab illustrates how to configure the ServiceHost declaratively using the service model configuration section. But how does the ServiceHost know which configuration section to use? When the ServiceHost is opened, it reads the <services> section looking for a <service> element that matches its service type. From the lab, consider this ServiceHost constructor:

ServiceHost myServiceHost = new ServiceHost(typeof(HelloIndigo.HelloIndigoService));

The ServiceHost will look for a <service> section using the name HelloIndigo.HelloIndigoService, as shown here:

<service behaviorConfiguration="serviceBehavior"name="HelloIndigo.HelloIndigoService" >
  <host>...</host>
  <endpoint... />
  <endpoint... />
</service>

The <service> element can include base addresses and service endpoints, as shown previously in Example 1-5. You can supply a base address for any protocol so that you can expose relative service endpoints over that protocol. The following illustrates the <host> section with base addresses for HTTP, TCP, and named pipe protocols:

<host>
  <baseAddresses>
    <add baseAddress="http://localhost:8000/HelloIndigo" />
    <add baseAddress="net.tcp://localhost:9000/HelloIndigo" />
    <add baseAddress="net.pipe://localhost/HelloIndigo" />
  </baseAddresses>
</host>

One or more <endpoint> sections may also be provided. As discussed previously, an endpoint is defined by an address, contract, and binding. If address is omitted altogether, the base address for the related binding protocol is used (and required). If the address omits the full URI, it is appended to the base address matching the binding protocol. However, you can specify a complete address that ignores the base address. The following illustrates these three choices for an endpoint configuration:

<endpoint binding="basicHttpBinding" name="basicHttp"
contract="Host.IHelloIndigoService" />
<endpointaddress="HelloIndigoService"
 binding="basicHttpBinding" name="basicHttp" contract="Host.IHelloIndigoService" />
<endpoint address="http://localhost:8001/HelloIndigo/HelloIndigoService"
 binding="basicHttpBinding" name="basicHttp" contract="Host.IHelloIndigoService" />

Endpoints have to be unique for a particular service. When multiple endpoints are exposed by a service, they must differ in address, contract, or transport protocol.

Tip

There are several reasons why a service may expose multiple endpoints, including the following:

  • The service implements multiple contracts, each requiring its own endpoint

  • The same or different service contracts must be accessible over multiple protocols

  • The same or different service contracts must be accessible by clients with different binding requirements, possibly related to security, reliable messaging, message size, or transactions

These topics will be explored throughout the book.

Enabling Metadata Exchange

A metadata exchange endpoint is required to support the dynamic generation of proxy and configuration for client applications. You must explicitly enable metadata exchange by adding the endpoint and enabling the metadata exchange behavior.

A metadata exchange (mex) endpoint is just like any other service endpoint in that it requires an address, contract, and binding. The address for a metadata exchange endpoint requires a base address for the selected binding protocol. The contract must be IMetadataExchange, a predefined service contract belonging to the System.ServiceModel.Description namespace (see Example 1-7).

Example 1-7. IMetadataExchange contract as defined by the service model

[ServiceContract(ConfigurationName="IMetadataExchange", Name="IMetadataExchange",
Namespace="http://schemas.microsoft.com/2006/04/mex")]

public interface IMetadataExchange
{
  [OperationContract(Action="http://schemas.xmlsoap.org/ws/2004/09/transfer/Get",
ReplyAction="http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse",
AsyncPattern=true)]
IAsyncResult BeginGet(Message request, AsyncCallback callback, object state);

  Message EndGet(IAsyncResult result);

  [OperationContract(Action="http://schemas.xmlsoap.org/ws/2004/09/transfer/Get",
ReplyAction="http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse")]Message Get(Message request);
}

As for the binding, there are several predefined mex bindings, including MexHttpBinding, MexHttpsBinding, MexTcpBinding, and MexNamedPipeBinding. That means you can expose a mex endpoint over HTTP, HTTPS, TCP, or named pipes and have SvcUtil consume those endpoints.

Tip

Like any other endpoint, metadata exchange endpoints can also be consumed at runtime by clients. Applications can call mex endpoints to dynamically generate proxies or just to request information about the associated service.

The following illustrates a service exposing two TCP endpoints: one for the service, another for metadata exchange:

<service behaviorConfiguration="serviceBehavior"
name="HelloIndigo.HelloIndigoService">
  <endpoint address="HelloIndigoService" binding="netTcpBinding" name="netTcp"
contract="HelloIndigo.IHelloIndigoService" />
  <endpoint binding="mexTcpBinding" name="mex" contract="IMetadataExchange" />
  <host>
    <baseAddresses>
      <add baseAddress="net.tcp://localhost:9000/HelloIndigo" />
    </baseAddresses>
  </host>
</service>

Supplying the endpoint is not sufficient on its own. The service metadata behavior must also be enabled. Example 1-5 shows you how to enable the behavior by associating a service behavior to the service and including the <serviceMetadata> element. Once the behavior is enabled, you can use SvcUtil for proxy generation against any mex endpoint. For example, to generate a service proxy and configuration using the TCP endpoint with SvcUtil, you can type this instruction at the command line:

svcutil /d:<YourLearningWCFPath>\Labs\Chapter1\HelloIndigo\Client /o:serviceproxy.cs
/config:app.confignet.tcp://localhost:9000/HelloIndigo/mex

Tip

It might seem a little annoying at first that you have to enable metadata exchange before you can generate a client proxy. This opt-in behavior is actually a good thing in the long run. You don’t want your services exposing endpoints of which you aren’t aware or that you don’t want to support.

Working with Behaviors

As you’ve seen, the ServiceHost is initialized by the <service> configuration section associated with its service type. At least one endpoint must be configured for the service to be useful to clients. In this lab, a single service endpoint and a metadata exchange endpoint are exposed—both over HTTP. While endpoints describe where to reach the service, which operations are available at the specified address, and what protocols are required—behaviors affect the service model locally at the client or service. What that means is that behaviors are not exposed as part of metadata, and they are not shared between clients and services. Instead, they locally affect how the service model processes messages.

Behaviors can be defined in configuration or in code. Different behaviors are available to clients and services, since the local affect on the service model also differs. There are two types of behaviors: service behaviors and endpoint behaviors. Service behaviors apply only to services, while endpoint behaviors can be associated with service endpoints or client endpoints.

Service behaviors

Service behaviors are types that implement IServiceBehavior from the System.ServiceModel.Description namespace. There are service behaviors to control debugging, metadata, security features, serialization, and throttling. When enabled, each behavior interacts with the service model to achieve its goal. For example, when the metadata behavior is enabled, the service model will allow requests to a metadata exchange endpoint. Otherwise, it will not.

Service behaviors are configured in the <serviceBehaviors> section. The following example illustrates enabling service debug and service metadata behaviors:

<behaviors>
    <serviceBehaviors>
      <behavior name="serviceBehavior">
        <serviceDebug includeExceptionDetailInFaults="true"/>
        <serviceMetadata />

      </behavior>
    </serviceBehaviors>
  </behaviors>

To associate a set of behaviors with a service use the behaviorConfiguration attribute of the <service> section (see Example 1-8).

Example 1-8. Associating a service behavior to a service

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behaviorname="serviceBehavior">
        <serviceMetadata />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <services>
    <service behaviorConfiguration="serviceBehavior"
name="Host.HelloIndigoService" >
    ...
    </service>
  </services>
</system.serviceModel>

Warning

You may forget to make the association between the service and behavior at least a few times. Don’t forget to double-check your configuration when you aren’t seeing the expected results at runtime!

You can also programmatically configure service behaviors through the ServiceHost instance. The Description property of the ServiceHost has a Behaviors collection. You can see if a behavior exists by calling the Find<T>() method on the collection. You can add new behaviors by calling Add() on the collection. Example 1-9 shows an example that looks to see if the ServiceMetadataBehavior exists, and if not adds it to the collection and enables browsing.

Example 1-9. Adding the metadata service behavior programmatically

ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService);

ServiceMetadataBehavior mexBehavior =
host.Description.Behaviors.Find<ServiceMetadataBehavior>();

if (mexBehavior == null)
{
  mexBehavior = new ServiceMetadataBehavior();
  mexBehavior.HttpGetEnabled = true;
  host.Description.Behaviors.Add(mexBehavior);
}

host.Open();

Endpoint behaviors

Endopint behaviors implement IEndpointBehavior, also from the System.ServiceModel.Description namespace. To configure proxies, for example, there are endpoint behaviors to control debugging, security, serialization, timeouts, and routing. These endpoint behaviors interact with the service model at the client and are configured in the <endpointBehaviors> section. The following example enables exception debugging for callbacks:

<behaviors>
  <endpointBehaviors>
    <behavior>
      <callbackDebug includeExceptionDetailInFaults="true"/>
    </behavior>
  </endpointBehaviors>
</behaviors>

Endpoint behaviors are associated with client endpoints using the behaviorConfiguration attribute of the <endpoint> section (see Example 1-10).

Example 1-10. Associating an endpoint behavior to a client endpoint

<system.serviceModel>
  <behaviors>
    <endpointBehaviors>
      <behaviorname="clientBehavior">
         . . .
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <client>
    <endpoint address="net.tcp://localhost:9000/" binding="netTcpBinding"
contract="Client.localhost.IHelloIndigoService"
behaviorConfiguration="clientBehavior">
    </endpoint>
  </client>
</system.serviceModel>

To programmatically configure endpoint behaviors at the client, you can use the object model of the client proxy. The Endpoint property of the client proxy has a Behaviors collection through which you can search for existing behaviors and add behaviors—similar to the way you would for the ServiceHost. Example 1-11 shows an example that looks to see if the ServiceDebugBehavior exists, and if not adds it to the collection.

Example 1-11. Adding the debug service behavior programmatically

HelloIndigoServiceClient proxy = new HelloIndigoServiceClient();

ServiceDebugBehavior debugBehavior =
proxy.Endpoint.Behaviors.Find<ServiceDebugBehavior>();

if (debugBehavior == null)
{
  debugBehavior = new ServiceDebugBehavior();

  debugBehavior.IncludeExceptionDetailInFaults = true;
  proxy.Endpoint.Behaviors.Add(debugBehavior);
}

Tip

I will explore other behaviors for the client and service throughout this book as I review features related to each behavior. At this point, I want you to understand how services and client endpoints are related to behaviors.

Proxy Initialization

For a client to invoke service operations, it must open a communication channel to a particular service endpoint. This channel is bound to a particular endpoint—its address, binding, and contract. This is done by creating a proxy.

In the first lab, a proxy is created directly by using the channel factory:

IHelloIndigoService proxy =
ChannelFactory<IHelloIndigoService>.CreateChannel(new BasicHttpBinding(), new
EndpointAddress("http://localhost:8000/HelloIndigo/HelloIndigoService"));

This approach assumes that:

  • You have prior knowledge of the endpoint address.

  • A copy of the service contract definition is locally available.

  • You have prior knowledge of the required protocols or binding configuration.

If you own both sides (client and service), it is feasible to share an assembly that contains service metadata and to separately communicate the address and binding requirements. When you don’t own both sides, generating the proxy is a more effective way to import the metadata necessary to construct the client channel. In this lab, the proxy is generated with SvcUtil. This proxy includes a generated copy of the service contract and a channel wrapper to simplify the client code necessary to consume the service. SvcUtil also generates the service model configuration necessary to initialize the proxy.

The service contract generated by SvcUtil looks similar to the contract at the service with the exception of additional details specified in the ServiceContractAttribute and OperationContractAttribute, shown in Example 1-12.

Example 1-12. Service contract generated by SvcUtil

[System.ServiceModel.ServiceContractAttribute(Namespace=
"http://www.thatindigogirl.com/samples/2006/06",
ConfigurationName="Client.localhost.IHelloIndigoService")]
public interface IHelloIndigoService
{
  [System.ServiceModel.OperationContractAttribute(Action=

"http://www.thatindigogirl.com/samples/2006/06/IHelloIndigoService/HelloIndigo",
ReplyAction=
"http://www.thatindigogirl.com/samples/2006/06
/IHelloIndigoService/HelloIndigoResponse")]
  string HelloIndigo();
}

The namespace specified by the ServiceContractAttribute is the same as at the service. This is critical to compatible message serialization.

The SvcUtil that generated the client-side service contract also generated a proxy type. The proxy type is a partial class that inherits ClientBase<T> from the System.ServiceModel namespace (T is the service contract type). As shown in bold in Example 1-13, the proxy exposes service contract operations and internally uses its reference to the client communication channel to invoke each service operation. In fact, this inner channel reference is like the one you previously created with the channel factory.

Example 1-13. Proxy type generated by SvcUtil

public partial class HelloIndigoServiceClient :
System.ServiceModel.ClientBase<Client.localhost.IHelloIndigoService>,
Client.localhost.IHelloIndigoService
{
  ...overloaded constructorspublic string HelloIndigo()
  {
    return base.Channel.HelloIndigo();
  }
}

When the first operation is invoked on this proxy, the inner channel is created based on the endpoint configuration for the proxy. Since in this lab only one endpoint is available, the construction of the proxy looks something like this:

HelloIndigoServiceClient proxy = new HelloIndigoServiceClient();

If there are several endpoints to choose from in the <client> configuration section, you are required to provide an endpoint configuration name to the proxy constructor:

// Proxy construction
HelloIndigoServiceClient proxy = new HelloIndigoServiceClient("basicHttp");

// Endpoint configuration
<endpoint address="http://localhost:8001/HelloIndigo/HelloIndigoService"
binding="basicHttpBinding" name="basicHttp" contract="Host.IHelloIndigoService" />

Once the client channel has been used (by invoking an operation), the proxy is bound to the endpoint configuration that initialized it. The same proxy instance cannot be used to invoke another service endpoint, and no changes to protocols or behaviors are allowed. A new proxy (channel) must be constructed if such changes are required.

Get Learning WCF now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.