WCF Quick Start


In this section, we pay homage to the computer science gods by building a Hello WCF messaging application. After building the application, we will dissect it to see the moving parts. To keep the example as simple as possible, we will combine the sender and receiver into a single console application. Let’s get started by building the infrastructure required in a console application:

 // File: HelloWCFApp.cs using System; sealed class HelloWCF {   static void Main(){   } }

Defining the Service Contract

The first WCF-specific step in building our HelloWCF application is creating the service contract. Contracts are covered in detail in Chapter 9, “Contracts”, but for now, suffice it to say that contracts are the primary way that we express the shape of our messaging application. By shape, I mean the operations our service exposes, the message schemas those operations produce and consume, and the Message Exchange Patterns (MEPs) each operation implements. In short, contracts define what our messaging application produces and consumes. Most contracts are type definitions annotated with attributes defined in the WCF application programming interface (API). In our example, the service contract is an interface annotated with the System.ServiceModel.ServiceContractAttribute and the System.ServiceModel.OperationContractAttribute, as shown here:

 // File: HelloWCFApp.cs [ServiceContract] public interface IHelloWCF {   [OperationContract]   void Say(String input); }

At a high level, our service contract states that our receiving application contains one operation named Say and that operation accepts a parameter of type String and has a void return type. A sending application can use this service contract as a means to construct and send messages to the receiving application. Now that we have defined the service contract, it’s time to add the code that defines where the receiving application will listen for incoming messages and how the application will exchange messages with other messaging participants.

Defining the Address and the Binding

Defining the location where our application listens for incoming messages requires us to use the System.Uri type, and defining how our application exchanges messages with other participants requires us to use the System.ServiceModel.Channels.Binding type or one of its derived types. The following code snippet illustrates how to use the Uri type and the Binding type in our application:

 // File: HelloWCFApp.cs static void Main(){   // define where to listen for messages   Uri address = new Uri("http://localhost:8000/IHelloWCF");   // define how to exchange messages   BasicHttpBinding binding = new BasicHttpBinding(); }

Notice that the address local variable indicates a Uniform Resource Identifier (URI) that uses the HTTP scheme. Choosing this address forces us to choose a binding that uses the HTTP transport. At a high level, a binding is the primary means we use to indicate the transport, the message choreographies, and the message encoder used in the application. The binding local variable is of type BasicHttpBinding. As you can tell from the name, BasicHttpBinding creates a messaging infrastructure that uses the HTTP transport.

Creating an Endpoint and Starting to Listen

Next we must use the address, the binding, and the contract to build an endpoint and then listen on that endpoint for incoming messages. In general, a WCF receiving application can build and listen on multiple endpoints, and each one requires an address, a binding, and a contract. The System.ServiceModel.ServiceHost type builds and hosts endpoints and manages other parts of the receiving infrastructure such as threading and object lifetime. The following code snippet demonstrates how to instantiate the ServiceHost type, how to add an endpoint, and how to begin listening for incoming messages:

   // File: HelloWCFApp.cs   static void Main(){     // define where to listen for messages     Uri address = new Uri("http://localhost:4000/IHelloWCF");     // define how to exchange messages     BasicHttpBinding binding = new BasicHttpBinding();     // instantiate a ServiceHost, passing the type to instantiate     // when the application receives a message     ServiceHost svc = new ServiceHost(typeof(HelloWCF));     // add an endpoint, passing the address, binding, and contract     svc.AddServiceEndpoint(typeof(IHelloWCF), binding, address);     // begin listening     svc.Open();     // indicate that the receiving application is ready and     // keep the application from exiting immediately     Console.WriteLine("The HelloWCF receiving application is ready");     Console.ReadLine();     // close the service host     svc.Close();   }

Also notice the argument in the call to the ServiceHost constructor. The ServiceHost constructor is overloaded several times, and each overload accepts, in some form or fashion, the type definition of the object that the WCF infrastructure dispatches incoming messages to. The ServiceHost constructor shown in the preceding code snippet indicates that the messaging infrastructure dispatches received messages to an instance of the HelloWCF type.

Also notice in the preceding code snippet the call to svc.AddServiceEndpoint and svc.Open. The AddServiceEndpoint instance method on the ServiceHost type sets the state of the ServiceHost object so that it will listen for incoming messages in a manner consistent with the address, the binding, and the contract parameters. It is important to note that the AddServiceEndpoint method does not begin the listening loop; it simply changes the state of the ServiceHost object (more on this in Chapter 10). The Open instance method on the ServiceHost type builds the messaging infrastructure and then begins the listening loop. The implementation of the Open method validates the state of the ServiceHost object, builds the endpoints from that state, and then begins the listening loop for each endpoint.

Mapping Received Messages to a HelloWCF Member

In its current form, our application will compile, but it will throw an InvalidOperationException when the application attempts to build the endpoint. The reason is fairly straightforward: in the constructor of the ServiceHost type, we passed the HelloWCF type as an argument, thereby signaling our intent for the messaging infrastructure to dispatch received messages to HelloWCF objects. For this to happen, there must be a mapping that associates received messages to members on the HelloWCF type. The simplest way to create this mapping is to change the HelloWCF type to implement the service contract defined by the IHelloWCF interface, as shown here:

 // File: HelloWCFApp.cs using System; using System.ServiceModel; using System.ServiceModel.Channels; // implement the IHelloWCF service contract sealed class HelloWCF : IHelloWCF {   // indicate when a HelloWCF object is created   HelloWCF() { Console.WriteLine("HelloWCF object created"); }   static void Main(){     // define where to listen for messages     Uri address = new Uri("http://localhost:4000/IHelloWCF");     // define how to exchange messages     BasicHttpBinding binding = new BasicHttpBinding();     // instantiate a ServiceHost, passing the type to instantiate     // when the application receives a message     ServiceHost svc = new ServiceHost(typeof(HelloWCF));     // add an endpoint, passing the address, binding, and contract     svc.AddServiceEndpoint(typeof(IHelloWCF), binding, address);     // begin listening     svc.Open();     // indicate that the receiving application is ready and     // keep the application from exiting immediately     Console.WriteLine("The HelloWCF receiving application is ready");     // wait for incoming messages     Console.ReadLine();     // close the service host     svc.Close();   }   // received messages are dispatched to this instance   // method as per the service contract   public void Say(String input){     Console.WriteLine("Message received, the body contains: {0}", input);   } } [ServiceContract] public interface IHelloWCF {   [OperationContract]   void Say(String input); }

Changing the HelloWCF type definition in this manner causes the messaging infrastructure to dispatch received messages to the Say instance method on the HelloWCF type, thereby output-ting a simple statement to the console.

Compiling, Running, and Verifying the Receiver

We are now ready to compile and run the application with the following command lines:

 C:\temp>csc /nologo /r:"c:\WINDOWS\Microsoft.Net\Framework\v3.0\Windows Communication Foundation\System.ServiceModel.dll" HelloWCFApp.cs C:\temp>HelloWCFApp.exe The HelloWCF receiving application is ready

At this point, the receiving application is passively waiting for incoming messages. We can verify that the application is indeed listening by running netstat.exe, as shown here:

 c:\temp>netstat –a –b TCP    kermit:4000            0.0.0.0:0              LISTENING       1104 [HelloWCFApp.exe]

This will no doubt produce more output than is shown in this example, but you should see two lines that look similar to these. (The name of my computer is Kermit.)

Sending a Message to the Receiver

The sending infrastructure relies on the address, binding, and contract constructs in much the same way the receiving infrastructure does. It is typically the sender’s responsibility to use an address, a binding, and a contract that are compatible with the ones the receiver uses. Given the simplicity of our application, the sender can simply reuse the address, binding, and contract that the receiving infrastructure uses.

The sending code, however, uses different types than the receiver does. Conceptually, this makes sense because the sender and receiver have distinctly different roles in the message exchange. Instead of using the Uri type directly, most senders rely on the System.Service-Model.EndpointAddress type as a means for expressing the target of a message. As you’ll see in Chapter 5, “Messages,” the EndpointAddress type is the WCF abstraction of a WS-Addressing endpoint reference. Furthermore, the sender does not use the ServiceHost type, but rather uses the ChannelFactory<T> type (where T is the service contract type). The ChannelFactory<T> type builds the sending infrastructure in much the same way that the ServiceHost type builds the receiving infrastructure. The following code snippet shows how to use the EndpointAddress type and the ChannelFactory<T> type to build the sending infrastructure:

 // File: HelloWCFApp.cs using System; using System.ServiceModel; using System.ServiceModel.Channels; // implement the IHelloWCF service contract sealed class HelloWCF : IHelloWCF {   // indicate when a HelloWCF object is created   HelloWCF() { Console.WriteLine("HelloWCF object created"); }   static void Main(){     // define where to listen for messages     Uri address = new Uri("http://localhost:4000/IHelloWCF");     // define how to exchange messages     BasicHttpBinding binding = new BasicHttpBinding();     // instantiate a ServiceHost, passing the type to instantiate     // when the application receives a message     ServiceHost svc = new ServiceHost(typeof(HelloWCF));     // add an endpoint, passing the address, binding, and contract     svc.AddServiceEndpoint(typeof(IHelloWCF), binding, address);     // begin listening     svc.Open();     // indicate that the receiving application is ready and     // keep the application from exiting immediately     Console.WriteLine("The HelloWCF receiving application is ready");     // begin the sender code     // create a channelFactory<T> with binding and address     ChannelFactory<IHelloWCF> factory =       new ChannelFactory<IHelloWCF>(binding,                                     new EndpointAddress(address));     // use the factory to create a proxy     IHelloWCF proxy = factory.CreateChannel();     // use the proxy to send a message to the receiver     proxy.Say("Hi there WCF");     // end the sender code     Console.ReadLine();     // close the service host     svc.Close();   }   // received messages are dispatched to this instance   // method as per the service contract   public void Say(String input){     Console.WriteLine("Message received, the body contains: {0}", input);   } } [ServiceContract] public interface IHelloWCF {   [OperationContract]   void Say(String input); }

Notice that we call the CreateChannel instance method on the ChannelFactory<T> object and use the object returned to invoke a method on our service contract interface. At a high level, the ChannelFactory<T> object is a type that can manufacture the sending infrastructure required to generate and send a message to the receiver (hence the need to pass the binding and address in the constructor). The CreateChannel instance method on the ChannelFac-tory<T> type actually creates the sending infrastructure and returns a reference to that infrastructure via an object whose type implements the service contract interface. We interact with this sending infrastructure by invoking the methods on our service contract interface. Keep in mind that there are several other ways to accomplish the same work, and we will explore these later in this chapter and again in Chapter 6, “Channels.”

Compiling, Running, and Verifying the Sender

Now that we have our receiving and sending infrastructure in place, it’s time to compile and run the application, as shown here:

 c:\temp>csc /nologo /r:"c:\WINDOWS\Microsoft.Net\Framework\v3.0\Windows Communication Foundation\System.ServiceModel.dll" HelloWCFApp.cs c:\temp>HelloWCFApp.exe The HelloWCF receiving application is ready HelloWCF object created Message received, the body contains: HelloWCF!

As expected, our application does the following at run time:

  1. Builds the infrastructure required to listen for incoming messages on http://localhost:4000/IHelloWCF

  2. Begins listening for incoming messages on http://localhost:4000/IHelloWCF

  3. Builds the infrastructure required to send a message to http://localhost:4000/IHelloWCF

  4. Generates and sends a message to http://localhost:4000/IHelloWCF

  5. Receives the message, instantiates a new HelloWCF object, and dispatches that message to the Say method on the HelloWCF object

Looking at the Message

On close inspection, none of the code in our HelloWCF example interacts with anything that even remotely resembles a message. To the application developer, a WCF application looks and feels much like any object-oriented or component-oriented application. At run time, however, a WCF application is fully engaged in the work of generating, sending, receiving, or otherwise processing messages. We can see the message that the WCF infrastructure generates by changing the implementation of the Say method to the following:

 public void Say(String input){   Console.WriteLine("Message received, the body contains: {0}", input);   // Show the contents of the received message   Console.WriteLine(     OperationContext.Current.RequestContext.RequestMessage.ToString()); }

The change to the Say method changes the application output to the following:

 The HelloWCF receiving application is ready HelloWCF object created Message received, the body contains: HelloWCF! <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">   <s:Header>     <To s:mustUnderstand="1"         xmlns="http://schemas.microsoft.com/ws/2005/05/adessing/none">       http://localhost:8000/IHelloWCF     </To>     <Action s:mustUnderstand="1"             xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">          http://tempuri.org/IHelloWCF/Say        </Action>   </s:Header>   <s:Body>     <Say xmlns="http://tempuri.org/">       <input>HelloWCF!</input>     </Say>   </s:Body> </s:Envelope>

Notice that the SOAP message is printed and the body of the SOAP message contains the String we passed to the Say method on the channel local variable. At the macroscopic level, this sending part of our application takes this String, uses it build a SOAP message, and then sends that SOAP message to the receiving part of our application. The receiving part of our application, on the other hand, receives the SOAP message, creates a HelloWCF object, extracts the contents of the SOAP body, and invokes the Say method on the HelloWCF object, passing the String as an argument.

A Slight Change with a Major Impact

The WCF infrastructure does most of the messaging work for us, and the normal object model does not always reveal the fact that our WCF application is actually passing messages between the sender and receiver. In fact, from the developer’s perspective, the code shown in our example looks more like an application that uses distributed objects than a messaging application. We can, however, very easily see that our HelloWCF application is indeed a messaging application by changing one line of code and observing the impact that change has on message composition.

If we change the line

 BasicHttpBinding binding = new BasicHttpBinding();

to the following:

 WSHttpBinding binding = new WSHttpBinding();

we see the following output:

 The HelloWCF receiving application is ready Creating and sending a message to the receiver HelloWCF object created Message received, the body contains: HelloWCF! <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"             xmlns:s="http://www.w3.org/2003/05/soap-envelope">   <s:Header>     <a:Action s:mustUnderstand="1" u:               xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-               wss-wssecurity-utility-1.0.xsd">          http://tempuri.org/IHelloWCF/Say        </a:Action>     <a:MessageID u:                  xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-                  200401-wss-wssecurity-utility-1.0.xsd">       urn:uuid:     </a:MessageID>     <a:ReplyTo u:                xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-                200401-wss-wssecurity-utility-1.0.xsd">       <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>     </a:ReplyTo>     <a:To s:mustUnderstand="1" u: xmlns:u="http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-wssecurity-utility-1.0.xsd">       http://localhost:8000/IHelloWCF     </a:To>     <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis- 200401-wss-wssecurity-secext-1.0.xsd">       <u:Timestamp u:                    xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-                    200401-wss-wssecurity-utility-1.0.xsd">         <u:Created>2006-08-29T01:57:50.296Z</u:Created>         <u:Expires>2006-08-29T02:02:50.296Z</u:Expires>       </u:Timestamp>       <c:SecurityContextToken u: xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc" xmlns:u="http://docs.oasis-open.org/wss/ 2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">         <c:Identifier>           urn:uuid:         </c:Identifier>       </c:SecurityContextToken>       <c:DerivedKeyToken          u:          xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc"          xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-          wssecurity-utility-1.0.xsd">         <o:SecurityTokenReference>           <o:Reference             ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct"                URI="#uuid--6" />         </o:SecurityTokenReference>         <c:Offset>0</c:Offset>         <c:Length>24</c:Length>         <c:Nonce>A170b1nKz88AuWmWYONX5Q==</c:Nonce>       </c:DerivedKeyToken>       <c:DerivedKeyToken         u:         xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc"         xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-           wssecurity-utility-1.0.xsd">         <o:SecurityTokenReference>           <o:Reference             ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct"                URI="#uuid--6" />         </o:SecurityTokenReference>         <c:Nonce>I8M/H2f3vFuGkwZVV1Yw0A==</c:Nonce>       </c:DerivedKeyToken>       <e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">         <e:DataReference URI="#_1" />         <e:DataReference URI="#_6" />       </e:ReferenceList>       <e:EncryptedData          Type="http://www.w3.org/2001/04/xmlenc#Element"         xmlns:e="http://www.w3.org/2001/04/xmlenc#">         <e:EncryptionMethod           Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />         <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">           <o:SecurityTokenReference>             <o:Reference               ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/dk"               URI="#uuid--11" />           </o:SecurityTokenReference>         </KeyInfo>         <e:CipherData>           <e:CipherValue> vQ+AT5gioRS6rRiNhWw2UJmvYYZpA+cc1DgC/K+6Dsd2enF4RUcwOG2 xqfkD/ EZkSFRKDzrJYBz8ItHLZjsva4kqfx3UsEJjYPKbxihl2GFrXdPwTmrHWt35Uw0L2rTh8kU9rtj44NfULS59CJbXE6PC7 Af1qWvnobcPXBqmgm4NA8wwSTuR3IKHPfD/Pg/ 3WABob534WD4T1DbRr5tXwNr+yQl2nSWN8C0aaP9+LCKymEK7AbeJXAaGoxdGu/ t6l7Bw1lBsJeSJmsd4otXcLxt976kBEIjTl8/ 6SVUd2hmudP2TBGDbCCvgOl4c0vsHmUC1SjXE5vXf6ATkMj6P3o0eMqBiWlG26RWiYBZ3OxnC1fDs60uSvfHtfF8CD0I LYGHLgnUHz5CFY0rPomT73RCkCfmgFuheCgB9zHZGtWedY6ivNrZe2KPx0ujQ2Mq4pv4bLns2qoykwK03ma7YGiGExGc ZBfkZ2YAkYmHWXJ0Xx4PJmQRAWIKfUCqcrR6lwyLjl5Agsrt0xHA5WEk3hapscW3HZ8wOgwv0fcHlZ1e3EAm0dZr5Ose 3TAKMXf7FC1tMy5u0763flA6AZk9l7IpAQXcTLYicriH5hzf1416xbTJCtt2rztiItSkYizkiJCUMJLanc6ST5i+GVHz J5oRCEWgfOTcQpHmri8y1P1+6jYe9ELla8Mj           </e:CipherValue>         </e:CipherData>       </e:EncryptedData>     </o:Security>   </s:Header>   <s:Body u: xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-     200401-wss-wssecurity-utility-1.0.xsd">     <Say xmlns="http://tempuri.org/">       <input>HelloWCF!</input>     </Say>   </s:Body> </s:Envelope>

As you can see, one simple change has a dramatic impact on the structure of the messages that our application produces. Changing from the BasicHttpBinding to the WSHttpBinding shifts our application from one that uses simple SOAP messages over HTTP to one that engages in a patchwork of WS-* protocols and choreographies over HTTP. The impact is more than just a more verbose and descriptive message, because our application is now sending and receiving multiple messages based on the WS-Security, WS-SecureConversation, and other specifications.

Note 

In effect, the macroscopic programming model for WCF completely removes all perspective that a WCF application is indeed a messaging application, and provides more of a distributed object “feel.” This is, in my opinion, a tremendous benefit of the platform, but it is, at the same time, fraught with danger. As developers, we must resist the temptation to lull ourselves into the idea that we can approach WCF as we would a distributed object platform, and embrace the concepts of messaging instead. Furthermore, as application and infrastructure developers, we must comprehend how changes in the way we use WCF types impact the messages that our application processes.

Exposing Metadata

Our Hello WCF application takes a very simplistic approach to creating compatibility between the receiver and the sender. Since both the receiver and the sender reside in the same AppDo-main and the objects that the receiver uses are visible to the sender, we simply reuse the address, binding, and contract in the sender. In most messaging applications, however, this approach is not feasible. In most cases, we can expect the sender and receiver to reside in different AppDomains on different computers. In these scenarios, the receiver typically dictates the messaging requirements, and the senders attempt to adhere to those requirements.

The WS-MetadataExchange specification dictates the terms of how the sender and receiver can exchange this information in a vendor agnostic manner. In more specific terms, the WS-MetadataExchange specification dictates message schemas and choreographies that facilitate the exchange of information about a messaging endpoint or endpoints. In most real-world applications (or at least ones that are more complex than our Hello WCF application), there is a need to expose this information in a way that a sender can interrogate a receiver’s endpoint to extract metadata and use that metadata to build the infrastructure necessary to send a compatible message to that endpoint.

By default, our Hello WCF application does not expose any metadata-at least, not in its most commonly accepted form of Web Services Description Language (WSDL) and Extensible Schema Definition (XSD). (Don’t confuse messaging application metadata with assembly or type metadata, even though one can be used to create the other.) In fact, WCF applications by default do not expose metadata, and the reason for this default is rooted in concerns for security. The information exposed by metadata often includes the security requirements for that application. In the name of protecting secrets, the team opted to turn this feature off by default.

If, however, we decide to expose our application’s metadata, we can build an endpoint specifically for exchanging metadata, and we approach building a metadata endpoint in much the same way that we approach building any endpoint: by starting with an address, a binding, and a contract. Unlike the endpoints you’ve seen so far, however, the service contract for a metadata endpoint is already defined for us in the WCF API.

The first step in building a metadata endpoint is to change the state of the ServiceHost in such a way that it expects to host metadata. We do this by adding a System.ServiceModel. Description.ServiceMetadataBehavior object to the behavior collection of the ServiceHost. A behavior is special information that the WCF infrastructure uses to change local message processing. The following code demonstrates how to add a ServiceMetadataBehavior object to the active ServiceHost:

 // instantiate a ServiceHost, passing the type to instantiate // when the application receives a message ServiceHost svc = new ServiceHost(typeof(HelloWCF), address); // BEGIN NEW METADATA CODE // create a ServiceMetadataBehavior ServiceMetadataBehavior metadata = new ServiceMetadataBehavior(); metadata.HttpGetEnabled = true; // add it to the servicehost description svc.Description.Behaviors.Add(metadata);

The next step is to define the Binding for the metadata endpoint. The object model for a metadata Binding is very different from other bindings-namely, we create the metadata Binding by calling a factory method on the System.ServiceModel.Description. MetadataExchangeBindings type, as shown here (other parts of our Hello WCF application have been omitted for clarity):

 // instantiate a ServiceHost, passing the type to instantiate // when the application receives a message ServiceHost svc = new ServiceHost(typeof(HelloWCF)); // BEGIN NEW METADATA CODE // create a ServiceMetadataBehavior ServiceMetadataBehavior metadata = new ServiceMetadataBehavior(); // add it to the servicehost description svc.Description.Behaviors.Add(metadata); // create a TCP metadata binding Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();

As a result of previous conditioning with ASMX, you might have the notion that metadata is expressible only over the HTTP transport. In reality, metadata is transmittable over a wide variety of transports, and WS-MetadataExchange states this flexibility. In our example, however, we call the CreateMexTcpBinding method, and it returns a reference to a Binding-derived type that uses the TCP transport. Since we are using the TCP transport, we must also ensure the the metadata address we choose uses the TCP scheme, as shown here:

 // instantiate a ServiceHost, passing the type to instantiate // when the application receives a message ServiceHost svc = new ServiceHost(typeof(HelloWCF)); // BEGIN NEW METADATA CODE // create a ServiceMetadataBehavior ServiceMetadataBehavior metadata = new ServiceMetadataBehavior(); // add it to the servicehost description svc.Description.Behaviors.Add(metadata); // create a TCP metadata binding Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding(); // create an address to listen on WS-Metadata exchange traffic Uri mexAddress = new Uri("net.tcp://localhost:5000/IHelloWCF/Mex");

Now that we have defined the address and the binding we want to use for our metadata endpoint, we must add the endpoint to the ServiceHost, in much the same way we did the first messaging endpoint. When adding a metadata endpoint, however, we use a service contract already defined in the WCF API named System.ServiceModel.Description.IMetadataExchange. The following code snippet shows how to add a metadata endpoint to the ServiceHost, using the appropriate address, binding, and contract:

 // instantiate a ServiceHost, passing the type to instantiate // when the application receives a message ServiceHost svc = new ServiceHost(typeof(HelloWCF)); // BEGIN NEW METADATA CODE // create a ServiceMetadataBehavior ServiceMetadataBehavior metadata = new ServiceMetadataBehavior(); // add it to the servicehost description svc.Description.Behaviors.Add(metadata); // create a TCP metadata binding Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding(); // create an address to listen on WS-Metadata exchange traffic Uri mexAddress = new Uri("net.tcp://localhost:5000/IHelloWCF/Mex"); // add the metadata endpoint svc.AddServiceEndpoint(typeof(IMetadataExchange),                        mexBinding,                        mexAddress); // END METADATA CODE

If we build and run our new Hello WCF application, we see that the application is indeed listening on two different addresses. One address is for servicing metadata requests, and the other is for the IHelloWCF.Say functionality. Let’s now turn our attention to how we can extract metadata from the metadata endpoint and use it to build the sending infrastructure in our application.

Consuming Metadata

The Microsoft .NET Framework SDK installs a highly versatile tool named svcutil.exe, and one of its capabilities is to interrogate a running messaging application and generate a proxy based on the information it collects. Internally, svcutil.exe uses the WS-MetadataExchange protocol, as well as the WSDL “get” semantics popularized with ASMX. Since our receiving application now exposes a metadata endpoint, we can point svcutil.exe to that running endpoint, and svcutil.exe will autogenerate a proxy type and configuration information compatible with the endpoints referred to in the metadata endpoint. When used in this way, svcutil.exe sends messages to a receiving application in a manner consistent with WS-MetadataExchange and transforms the ensuing reply into .NET Framework types that facilitate the development of a sending application.

Generating the Proxy with Svcutil.exe

Before you run svcutil.exe, verify that the HelloWCFApp.exe receiving application is started and listening for incoming messages. Next open a new Windows SDK Command Prompt window, and enter the following command:

 C:\temp>svcutil /target:code net.tcp://localhost:5000/IHelloWCF/Mex

Svcutil.exe will create two files: HelloWCFProxy.cs and output.config. If you examine the HelloWCFProxy.cs file, you’ll see that svcutil.exe generated a source file that contains definitions for the IHelloWCF interface, an interface named IHelloWCFChannel, and a type named HelloWCFClient.

Note 

Of all the types autogenerated by svcutil.exe, the HelloWCFClient type is intended for the most frequent use. In my opinion, appending the word Client to the name of this type is a mistake in style that will undoubtedly surface as misunderstandings in the developer community. Without a doubt, Client connotes the phrase Client and Server. The HelloWCFClient type helps build a messaging infrastructure, not a traditional client/server infrastructure. Keep in mind that even though the name of this type ends in Client, we are still building a messaging application.

Together, these type definitions help us write sending code that is compatible with the receiver. Notice that there is no information in the HelloWCF.cs file about the address that the receiving application is listening on, nor a binding that is compatible with the receiving application in the HelloWCF.cs source file. This information is stored in the other file generated by svcutil.exe (output.config). WCF has a rich configuration infrastructure that allows us to configure many facets of a sending or receiving application through XML configuration files. To illustrate how to take advantage of the data created for us by svcutil, let’s create another console application that sends messages to the receiver. We will name this application HelloWCFSender. To do this, we we will have to rename the output.config file so that our new sending application reads the config file (change to HelloWCFSender.exe.config).

Coding HelloWCFSender with Svcutil.exe-Generated Types

In short, svcutil.exe has generated most of the source code and configuration settings we will need to write our new sending application. Creating this sending application is very similar to the one in HelloWCF.exe.

 using System; using System.ServiceModel; sealed class HelloWCFSender {   static void Main(){     // wait for the receiver to start     Console.WriteLine("Press ENTER when the Receiver is ready");     Console.ReadLine();     // print to the console that we are sending a message     Console.WriteLine("Sending a message to the Receiver");     // create the HelloWCFClient type created by svcutil     HelloWCFClient proxy = new HelloWCFClient();     // invoke the Say method     proxy.Say("Hi there from a new Sender");     proxy.Close();     // print to the console that we have sent a message     Console.WriteLine("Finished sending a message to the Receiver");   } }

Notice that we only have to instantiate the HelloWCFClient type and call the Say method. The real heavy lifting has been done by the types that svcutil.exe created and the WCF configuration infrastructure. After we have written this source code, we can compile it to an assembly with the following command line:

 C:\temp>csc /r: "C:\WINDOWS\Microsoft.Net\v3.0\Windows Communication Foundation\System.ServiceModel.dll" HelloWCFProxy.cs HelloWCFSender.cs

Next we start the receiving application (HelloWCFApp.exe) and then start the sender (HelloWCFSender.exe), and we see output like the following on the sender:

 C:\temp>HelloWCFSender.exe Press ENTER when the Receiver is ready Sending a message to the Receiver Finished sending a message to the Receiver

In a nutshell, the output from our application confirms that the sending part of our application is working as it did before, without reusing the objects we used to build the receiving part of our application. We can check the receiving application to verify that the recever did indeed receive a new message.

Now that we have two fully functioning WCF applications, let’s take a look at the architecture of WCF as a whole.




Inside Windows Communication Foundation
Inside Windows Communication Foundation (Pro Developer)
ISBN: 0735623066
EAN: 2147483647
Year: 2007
Pages: 106
Authors: Justin Smith

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net