Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp 2008 And The .NET 3.5 Platform [eng]-1

.pdf
Скачиваний:
34
Добавлен:
16.08.2013
Размер:
22.5 Mб
Скачать

892 CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

Table 25-8. Select Members of the ServiceHost Type

Members

Meaning in Life

Authorization

This property gets the authorization level for the service being hosted.

AddServiceEndpoint()

This method allows you to programmatically register an endpoint to the

 

host.

BaseAddresses

This property obtains the list of registered base addresses for the current

 

service.

BeginOpen()

These methods allow you to asynchronously open and close a

BeginClose()

ServiceHost object, using the standard asynchronous .NET delegate

 

syntax.

CloseTimeout

This property allows you to set and get the time allowed for the service to

 

close down.

Credentials

This property obtains the security credentials used by the current

 

service.

EndOpen()

These methods are the asynchronous counterparts to BeginOpen()

EndClose()

and BeginClose().

OpenTimeout

This property allows you to set and get the time allowed for the service to

 

start up.

State

This property gets a value that indicates the current state of the

 

communication object, represented by the CommunicationState enum

 

(opened, closed, created, etc.).

 

 

To illustrate some additional aspects of ServiceHost, update your Program class with a new static method that prints out various aspects of the current host:

static void DisplayHostInfo(ServiceHost host)

{

Console.WriteLine(); Console.WriteLine("***** Host Info *****");

Console.WriteLine("Name: {0}", host.Description.ConfigurationName);

Console.WriteLine("Port: {0}", host.BaseAddresses[0].Port);

Console.WriteLine("LocalPath: {0}", host.BaseAddresses[0].LocalPath);

Console.WriteLine("Uri: {0}", host.BaseAddresses[0].AbsoluteUri);

Console.WriteLine("Scheme: {0}", host.BaseAddresses[0].Scheme);

Console.WriteLine("**********************");

Console.WriteLine();

}

Assuming you call this new method from within Main() after opening your host:

using (ServiceHost serviceHost = new ServiceHost(typeof(MagicEightBallService)))

{

// Open the host and start listening for incoming messages. serviceHost.Open();

DisplayHostInfo(serviceHost);

...

}

CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

893

you will see the statistics shown in Figure 25-7.

Figure 25-7. Details of our host

Details of the <system.serviceModel> Element

Like any XML element, <system.serviceModel> can define a set of subelements, each of which can be qualified via numerous attributes. While you should consult the .NET Framework 3.5 SDK documentation for full details regarding the set of possible attributes, here is a skeleton that lists the valid subelements:

<system.serviceModel>

<behaviors>

</behaviors>

<client>

</client>

<commonBehaviors>

</commonBehaviors>

<diagnostics>

</diagnostics>

<serviceHostingEnvironment>

</serviceHostingEnvironment>

<comContracts>

</comContracts>

<services>

</services>

<bindings>

</bindings>

</system.serviceModel>

You’ll see more exotic configuration files as you move through the chapter; however, the crux of each subelement can be discovered in Table 25-9.

Table 25-9. Subelements of <service.serviceModel>

Subelement

Meaning in Life

behaviors

WCF supports various endpoint and service behaviors. In a

 

nutshell, a behavior allows you to further qualify the functionality

 

of a host or client.

bindings

This element allows you to fine-tune each of the WCF-supplied

 

bindings (basicHttpBinding, netMsmqBinding, etc.) as well as specify

 

any custom bindings used by the host.

Continued

894 CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

Table 25-9. Continued

Subelement

Meaning in Life

client

This element contains a list of endpoints a client uses to connect to

 

a service. Obviously, this is not terribly useful in a host’s *.config

 

file.

comContracts

This element defines COM contracts enabled for WCF and COM

 

interoperability.

commonBehaviors

This element can only be set within a machine.config file. It can be

 

used to define all of the behaviors used by each WCF service on a

 

given machine.

diagnostics

This element contains settings for the diagnostic features of WCF.

 

The user can enable/disable tracing, performance counters, and

 

the WMI provider, and can add custom message filters.

serviceHostingEnvironment

This element specifies if this operation can be the initial operation

 

in a session.

services

This element contains a collection of WCF services exposed from

 

the host.

 

 

Enabling Metadata Exchange

Recall that WCF client applications communicate with the WCF service via an intervening proxy type. While you could most certainly author the proxy code completely by hand, doing so would be tedious and error-prone. Ideally, a tool could be used to generate the necessary grunge code (including the client-side *.config file). Thankfully, the .NET Framework 3.5 SDK provides a com- mand-line tool (svcutil.exe) for this very purpose. As well, Visual Studio 2008 provides similar functionality via the Project Add Service Reference menu option.

However, in order for these tools to generate the necessary proxy code/*.config file, they must be able to discover the format of the WCF service interfaces and any defined data contracts (the method names, type of parameters, etc.).

Metadata exchange (MEX) is a WCF service behavior that can be specified to fine-tune how the WCF runtime handles your service. Simply put, each <behavior> element can define a set of activities a given service can subscribe to. WCF provides numerous behaviors out of the box, and it is possible to build your own.

The MEX behavior (which is disabled by default) will intercept any metadata requests sent via HTTP GET. If you want to allow svcutil.exe or Visual Studio 2008 to automate the creation of the required client-side proxy *.config file, you must enable MEX.

Enabling MEX is a matter of tweaking the host’s *.config file with the proper settings (or authoring the corresponding C# code). First, you must add a new <endpoint> just for MEX. Second, you need to define a WCF behavior to allow HTTP GET access. Third, you need to associate this behavior by name to your service via the behaviorConfiguration attribute on the opening <service> element. Finally, you need to add a <host> element to define the base address of this service (MEX will look here to figure out the locations of the types to describe).

Note This final step can be bypassed if you pass in a System.Uri object to represent the base address as a parameter to the ServiceHost constructor.

CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

895

Consider the following updated host *.config file, which creates a custom <behavior> element (named EightBallMEXBehavior) that is associated to our service via the behaviorConfiguration attribute within the <service> definition:

<?xml version="1.0" encoding="utf-8" ?> <configuration>

<system.serviceModel>

<services>

<service name="MagicEightBallServiceLib.MagicEightBallService" behaviorConfiguration = "EightBallServiceMEXBehavior">

<endpoint address ="" binding="basicHttpBinding"

contract="MagicEightBallServiceLib.IEightBall"/>

<!-- Enable the MEX endpoint -->

<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />

<!-- Need to add this so MEX knows the address of our service -->

<host>

<baseAddresses>

<add baseAddress ="http://localhost:8080/MagicEightBallService"/> </baseAddresses>

</host>

</service>

</services>

<!--

A behavior definition for MEX --

>

<behaviors>

<serviceBehaviors>

<behavior name="EightBallServiceMEXBehavior" > <serviceMetadata httpGetEnabled="true" />

</behavior>

</serviceBehaviors>

</behaviors>

</system.serviceModel>

</configuration>

You are now able to restart the service and view its metadata description using the web browser of your choice. To do so, while the host is still running simply enter the address as the URL:

http://localhost:8080/MagicEightBallService

Once you are at the homepage for your WCF service (see Figure 25-7), you are provided with basic details regarding how to interact with this service programmatically as well as a way to view the WSDL contract by clicking the hyperlink at the top of the page. Recall that Web Service Description Language (WSDL) is a grammar that describes the structure of web services at a given endpoint.

Source Code The MagicEightBallServiceHost project is located under the Chapter 25 subdirectory.

896 CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

Figure 25-8. Ready to view metadata, via MEX

Building the WCF Client Application

Now that our host is in place, the final task is to build a piece of software to communicate with this WCF service type. While we could take the long road and build the necessary infrastructure by hand (a feasible but labor-intensive task), the .NET Framework 3.5 SDK provides several approaches to quickly generate a client-side proxy. To begin, create a new Console Application named MagicEightBallServiceClient.

Generating Proxy Code Using svcutil.exe

The first way you can build a client-side proxy is to make use of the svcutil.exe command-line tool. Using svcutil.exe, you can generate a new C# language file that represents the proxy code itself as well as a client-side configuration file. To do so, simply specify the service’s endpoint as the first parameter. The /out: flag is used to define the name of the *.cs file containing the proxy, while the /config: option specifies the name of the generated client-side *.config file.

Assuming your service is currently running, the following command set passed into svcutil. exe will generate two new files in the working directory (which should, of course, be entered as a single line within a Visual Studio 2008 command prompt):

svcutil http://localhost:8080/MagicEightBallService /out:myProxy.cs /config:app.config

CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

897

If you open the myProxy.cs file, you will find a client-side representation of the IEightBall interface, as well as a new class named EightBallClient, which is the proxy class itself. This class derives from the generic class, System.ServiceModel.ClientBase<T>, where T is the registered service interface.

In addition to a number of custom constructors, each method adorned with the [OperationContract] attribute will be implemented to delegate to the parent class’s Channels property to invoke the correct external method. Here is a partial snapshot of the proxy type:

[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public partial class EightBallClient :

System.ServiceModel.ClientBase<IEightBall>, IEightBall

{

...

public string ObtainAnswerToQuestion(string userQuestion)

{

return base.Channel.ObtainAnswerToQuestion(userQuestion);

}

}

When you create an instance of the proxy type, the base class will establish a connection to the endpoint using the settings specified in the client-side application configuration file. Much like the server-side configuration file, the generated client-side App.config file contains an <endpoint> element and details regarding the basicHttpBinding used to communicate with the service.

In addition, you will find the following <client> element, which (once again) establishes the ABCs from the client’s perspective:

<client>

<endpoint

address="http://localhost:8080/MagicEightBallService" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IEightBall" contract="ServiceReference.IEightBall" name="BasicHttpBinding_IEightBall" />

</client>

At this point, you could include these two files into a client project (and reference the System. ServiceModel.dll assembly) and use the proxy type to communicate with the remote WCF service. However, rather than doing so, let’s see how Visual Studio can help to further automate the creation of client-side proxy files.

Generating Proxy Code Using Visual Studio 2008

Like any good command-line tool, svcutil.exe provides a great number of options that can be used to control how the client proxy is generated. If you do not require these advanced options, you are able to generate the same two files using the Visual Studio 2008 IDE. Simply select the Add Service Reference option from the Project menu.

Once you activate this menu option, you will be prompted to enter the service URI. At this point click the Go button to see the service description (see Figure 25-9).

898 CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

Figure 25-9. Generating the proxy files using Visual Studio 2008

Beyond creating and inserting the proxy files into your current project, this tool is kind enough to reference the WCF assemblies automatically on your behalf. As a naming convention, the proxy class is defined within a namespace called ServiceReference, which is nested in the client’s namespace (to avoid possible name clashes). Here, then, is the complete client code:

// Location of the proxy.

using MagicEightBallServiceClient.ServiceReference;

namespace MagicEightBallServiceClient

{

class Program

{

static void Main(string[] args)

{

Console.WriteLine("***** Ask the Magic 8 Ball *****\n");

using (EightBallClient ball = new EightBallClient())

{

Console.Write("Your question: "); string question = Console.ReadLine(); string answer =

ball.ObtainAnswerToQuestion(question); Console.WriteLine("8-Ball says: {0}", answer);

}

Console.ReadLine();

}

}

}

CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

899

Now, assuming your WCF host is currently running, you can execute the client. Figure 25-10 shows one possible response (apologies to Grace Wong at Apress).

Figure 25-10. The completed WCF client host

Source Code The MagicEightBallServiceClient project is located under the Chapter 25 subdirectory.

Configuring a TCP-Based Binding

At this point, the host and client applications are both configured to make use of the simplest of the HTTP-based bindings, basicHttpBinding. Recall that the benefit of offloading settings to configuration files is that we can change the underlying plumbing in a declarative manner.

To illustrate, let’s try a little experiment. Create a new folder on your C drive (or wherever you happen to be saving your code) named EightBallTCP, and within this new folder create two subdirectories named Host and Client.

Next, using Windows Explorer, navigate to the \bin\Debug folder of the host project and copy MagicEightBallServiceHost.exe, MagicEightBallServiceHost.exe.config, and

MagicEightBallServiceLib.dll to the C:\EightBallTCP\Host folder. Open the *.config file for editing using a simple text editor, and modify the existing contents as follows:

<?xml version="1.0" encoding="utf-8" ?> <configuration>

<system.serviceModel>

<services>

<service name="MagicEightBallServiceLib.MagicEightBallService"> <endpoint address =""

binding="netTcpBinding"

contract="MagicEightBallServiceLib.IEightBall"/>

<host>

<baseAddresses>

<add baseAddress ="net.tcp://localhost:8080/MagicEightBallService"/> </baseAddresses>

</host>

</service>

</services>

</system.serviceModel>

</configuration>

Essentially, this host’s *.config file has stripped out all the MEX settings (as we already built the proxy) and established that it is using the netTcpBinding binding type. Now run the application by double-clicking the *.exe. If all is well, you should find the host output shown in Figure 25-11.

900 CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

Figure 25-11. Hosting the WCF service using TCP bindings

To complete the test, copy the MagicEightBallServiceClient.exe and

MagicEightBallServiceClient.exe.config files from the \bin\Debug folder of the client application into the C:\EightBallTCP\Client folder. Update the client configuration file as so:

<?xml version="1.0" encoding="utf-8" ?> <configuration>

<system.serviceModel>

<client>

<endpoint address="net.tcp://localhost:8080/MagicEightBallService" binding="netTcpBinding" contract="ServiceReference.IEightBall" name="netTcpBinding_IEightBall" />

</client>

</system.serviceModel>

</configuration>

This client-side configuration file is a massive simplification from what the Visual Studio proxy generator authored. Notice how we have completely removed the existing <bindings> element. Originally, the *.config file contained a <bindings> element with a <basicHttpBinding> subelement that supplied numerous details of the client’s binding settings (timeouts, etc.).

In reality, we never needed that detail for our example, as we automatically obtain the default values of the underlying BasicHttpBinding object. If we needed to, we could of course update the existing <bindings> element to define details of the <netTcpBinding> subelement, but doing so is not required if we are happy with the default values of the NetTcpBinding object.

In any case, you should now be able to run your client application, and assuming the host is still running in the background, you are able to move data between your assemblies using TCP.

Source Code The MagicEightBallTCP project is located under the Chapter 25 subdirectory.

Using the WCF Service Library Project Template

Before we build a more exotic WCF service that communicates with our AutoLot database created in Chapter 22, the next example will illustrate a number of important topics, including the benefits of the WCF Service Library project template, the WCF Test Client, the WCF configuration editor, hosting WCF services within a Windows service, and asynchronous client calls. To stay focused on these new concepts, this WCF service will also be intentionally simple.

CHAPTER 25 INTRODUCING WINDOWS COMMUNICATION FOUNDATION

901

Building a Simple Math Service

To begin, create a brand-new WCF Service Library project named MathServiceLibrary, being sure to select the correct option under the WCF node of the New Project dialog box (see Figure 25-2 if you need a nudge). Now change the name of the initial IService1.cs file to IBasicMath.cs. Once you have done so, delete all of the example code within the MathServiceLibrary namespace and replace it with the following:

namespace MathServiceLibrary

{

[ServiceContract(Namespace="www.Intertech.com")] public interface IBasicMath

{

[OperationContract] int Add(int x, int y);

}

}

Next, change the name of the Service1.cs file to MathService.cs, and (once again) delete all the example code within the MathServiceLibrary namespace and implement your service contract as so:

namespace MathServiceLibrary

{

public class MathService : IBasicMath

{

public int Add(int x, int y)

{

// To simulate a lengthy request.

System.Threading.Thread.Sleep(5000); return x + y;

}

}

}

Finally, open the supplied App.config file and replace all occurrences of IService1 with IBasicMath, and all occurrences of Service1 with MathService. As well, take a moment to notice that this *.config file has already been enabled to support MEX, and by default it is making use of the wsHttpBinding protocol.

Testing the WCF Service with WcfTestClient.exe

One benefit of using the WCF Service Library project is that when you debug or run your library, it will read the settings in the *.config file and use them to load the WCF Test Client application (WcfTestClient.exe). This GUI-based application allows you to test each member of your service interface as you build the WCF service, rather than having to manually build a host/client as you did previously simply for testing purposes.

Figure 25-12 shows the testing environment for MathService. Notice that when you doubleclick an interface method, you are able to specify input parameters and invoke the member.

While this utility works out of the box when you have created a WCF Service Library project, be aware that you can use this tool to test any WCF service when you start it at the command line by specifying a MEX endpoint. For example, if you were to start the MagicEightBallServiceHost.exe application, you would be able to specify the following command within a Visual Studio 2008 command prompt:

wcftestclient http://localhost:8080/MagicEightBallService