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.
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.
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.
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.
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.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();
}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"; } }
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(); { } }
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.
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.
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>
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:
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.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 beHost.IHelloIndigoService
. Click Next to continue.Select HTTP as your service communication mode and click Next.
Select Basic Web Services interoperability as your interoperability method and click Next.
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.
Review the configuration you have chosen for the service, then click Finish.
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.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 selectserviceMetadata
from the list provided.Go to the Configuration pane and you’ll now see a new
serviceMetadata
node beneathserviceBehavior
. Select the node and review the default settings in the General tab.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 theBehaviorConfiguration
property toserviceBehavior
(you can select it from the dropdown list).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 tomexHttpBinding
. For the Contract property type,IMetadataExchange
.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 beneathHost.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.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.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.
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.
Go to the
Client
project and from Solution Explorer, right-click on theClient
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 tolocalhost
. 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.
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 theMain()
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’sHelloIndigo
operation. After testing, stop debugging theClient
project and close theHost
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.
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.
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
.Rename IService1.cs and Service1.cs to IHelloIndigoService.cs and HelloIndigoServices.cs, respectively.
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(); }
Now, modify the service implementation so that it implements the new contract. Open HelloIndigoServices.cs, rename the service to
HelloIndigoService
and implementIHelloIndigoService
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.
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.
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 theHelloIndigo
library you’re about to reference.Add a reference to the
HelloIndigo
class library project. Right-click on theHost
node and select Add Reference. From the Projects tab, select theHelloIndigo
project.The
ServiceHost
must be modified to refer to the service type from this project. In the Program.cs file, modify theServiceHost
constructor to use the fully qualified name of the service,HelloIndigo.HelloIndigoService
, as shown here:host = new ServiceHost(typeof(
HelloIndigo.HelloIndigoService
));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>Test the solution again by compiling and running the
Host
and then theClient
.
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.
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.
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/ HelloIndigoIf 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/HelloIndigoTo 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())
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.
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.
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.
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.
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.
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 |
<client> |
This element contains one or more |
<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 |
<behaviors> |
This element contains |
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.
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" /> <endpointaddress="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.
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.
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 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> <servicebehaviorConfiguration="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();
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.
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.