Using a Binding


Now that you’ve seen the types that make bindings work, let’s use a Binding to send and receive a Message. Although most WCF applications start with an address, a binding, and a contract, we are going to start this example more simply. In essence, this example will use a Binding to send and receive a Message without the help of most of the ServiceModel layer commonly associated with basic samples. In our example, we are going to use the BasicHttpBinding to send a message to a receiver and await a reply.

The receiver is the first part of the application that we need to build. To start, let’s instantiate a BasicHttpBinding object and assign values for the IDefaultCommunicationTimeouts properties. After the binding is built and the time-outs are set, we’ll create a Uri object to represent the address that our receiver will listen on. So far, our example looks like this:

 using System; using System.Collections.Generic; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; internal sealed class App {   static void Main(){     // create a binding     BasicHttpBinding binding = new BasicHttpBinding();     // set timeouts to large numbers for test purposes     binding.OpenTimeout = TimeSpan.FromDays(1);     binding.ReceiveTimeout = TimeSpan.FromDays(2);     binding.SendTimeout = TimeSpan.FromDays(3);     binding.CloseTimeout = TimeSpan.FromDays(4);     // create an address     Uri address = new Uri("http://localhost:4000/MyListener");     PrintHelper.Print("BUILDING THE RECEIVER");   } }

Next we need to use the Binding to create the channel listener stack. There are a few ways to do this. For the sake of simplicity, we will use the BuildChannelListener<TChannel> method on the BasicHttpBinding type. First, however, we must decide on the channel shape that our receiving channels will use. Because HTTP forces the use of the request/reply MEP, our choice is between IReplyChannel and IReplySessionChannel. As it turns out, the BasicHttpBinding creates messaging infrastructure that is not session capable, so that rules out IReplySessionChannel. After we have created the channel listener stack, we need to open the channel listener. With this in mind, our receiving application becomes the following:

 using System; using System.Collections.Generic; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; internal sealed class App {   static void Main(){     // create a binding     BasicHttpBinding binding = new BasicHttpBinding();     // set timeouts to large numbers for test purposes     binding.OpenTimeout = TimeSpan.FromDays(1);     binding.ReceiveTimeout = TimeSpan.FromDays(2);     binding.SendTimeout = TimeSpan.FromDays(3);     binding.CloseTimeout = TimeSpan.FromDays(4);     // create an address     Uri address = new Uri("http://localhost:4000/MyListener");     PrintHelper.Print("BUILDING THE RECEIVER");     // use the Binding to create a channel listener stack     // pass the address and an empty BindingParameterCollection as args     IChannelListener<IReplyChannel> listenerStack =       binding.BuildChannelListener<IReplyChannel>(address,         new BindingParameterCollection());     // open the channel listener stack     listenerStack.Open();   } }

Now that the state of the channel listener stack is CommunicationState.Opened, we need to use the channel listener stack to create a channel stack. As you saw in Chapter 7, the AcceptChannel method and its asynchronous variant return a channel stack. The AcceptChannel method on the channel listener stack created by the BasicHttpBinding does not wait for an incoming connection before returning. Instead, it simply returns a channel stack that might or might not have a message to receive. The channel listener stacks created by the MsmqIntegrationBinding and the NetMsmqBinding also behave this way. Connection-oriented channel listener stacks like the ones created by NetTcpBinding and NetNamedPipeBinding do not behave in this way. In cases where the AcceptChannel method blocks until a sending connection is made, it is a far better idea to use BeginAcceptChannel as opposed to AcceptChannel. Because this is a simple example and AcceptChannel does not wait for a sending connection, we will use the AcceptChannel method. After the AcceptChannel method returns, we will have a reference to the receiving channel stack. As with the channel listener stack, we will also have to open the receiving channel stack. With this in mind, our example becomes the following:

 // other code omitted for clarity // ... // open the channel listener stack listenerStack.Open(); // BasicHttp will return (no sessions) // 2 day timeout from the Binding (ReceiveTimeout) IReplyChannel receiveChannelStack = listenerStack.AcceptChannel(); // Open the channel stack (1 day timeout) receiveChannelStack.Open();

Notice in the preceding code that we are not calling the AcceptChannel or Open methods that accept TimeSpan arguments. In the channel listener stack and the channel stack, the methods that do not have TimeSpan arguments call the methods that do have TimeSpan arguments. In the case of the AcceptChannel method, the value of the DefaultReceivingTimeout is used. In the case of the Open method, the value of the DefaultOpenTimeout is used. Both of the values for these time-outs propagate from the binding.

Now that the state of our receiving channel stack is CommunicationState.Opened, let’s try to receive a Message. Because we are going to write our sending application in the same Main method as our receiving application, it is important for the message receive to happen asynchronously. To this end, we will call the BeginReceiveRequest method on the receiving channel stack. In the AsyncCallback delegate, we will need to call EndReceiveRequest, read the received Message, generate a Reply, and then close the RequestContext and channel stack. Our code now looks like the following:

 // Open the channel stack (1 day timeout) receiveChannels.Open(); // receive a request on another thread receiveChannels.BeginReceiveRequest(new AsyncCallback(receiveRequest),                                     receiveChannels); // end of the Main method } // the AsyncCallback for BeginReceiveRequest private static void receiveRequest(IAsyncResult ar) {   // get the channel stack   IReplyChannel channels = (IReplyChannel) ar.AsyncState;   // get the requestContext   RequestContext context = channels.EndReceiveRequest(ar);   // show the received message   PrintHelper.Print(String.Format("Message received:\n{0}",     context.RequestMessage.ToString()));   // create a reply Message   Message reply = Message.CreateMessage(MessageVersion.Soap11, "urn:SomeReplyAction", "Message back back");   // send the reply   context.Reply(reply);   // close the context   context.Close();   // close the channels   channels.Close(); }

At this point, the receiving part of our application is complete. Now let’s build the sending part of our application. The first thing we need to build is our channel factory stack. For that, we return to our Binding and call the BuildChannelFactory<TChannel> method. The channel factory stack returned from this method must then be opened. After opening the channel factory stack, we then create a sending channel stack by calling the CreateChannel method on the channel factory stack. After we have a reference to the sending channel stack, we must then open it. The following code sample has these steps in place:

 receiveChannels.BeginReceiveRequest(new AsyncCallback(receiveRequest),                                     receiveChannels); // create the channel factory stack IChannelFactory<IRequestChannel> channelFactoryStack =     binding.BuildChannelFactory<IRequestChannel>(       new BindingParameterCollection()); // open the channel factory stack channelFactoryStack.Open(); // create the channel stack from the channel factory stack // pass a new EndpointAddress to set the target of the Message IRequestChannel sendChannels = channelFactoryStack.CreateChannel(   new EndpointAddress(address)); // open the channel stack sendChannels.Open(); // end of the Main method

Now that our sending channels are open, we are free to call their Request or BeginRequest method. These methods send a Message to the receiving application and wait for a reply. The Request method blocks until a reply is received, and the BeginRequest method is asynchronous. Because this is the final task of our application and we cannot accept any user input, we will use the Request method. With this in place, the entire final example application is shown here:

 using System; using System.Collections.Generic; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; internal sealed class App {   static void Main(){     // create a binding     BasicHttpBinding binding = new BasicHttpBinding();     // set timeouts to large numbers for test purposes     binding.OpenTimeout = TimeSpan.FromDays(1);     binding.ReceiveTimeout = TimeSpan.FromDays(2);     binding.SendTimeout = TimeSpan.FromDays(3);     binding.CloseTimeout = TimeSpan.FromDays(4);     // create an address     Uri address = new Uri("http://localhost:4000/MyListener");     PrintHelper.Print("BUILDING THE RECEIVER");     // use the Binding to create a channel listener stack     // pass the address and an empty BindingParameterCollection as args     IChannelListener<IReplyChannel> listenerStack =       binding.BuildChannelListener<IReplyChannel>(address,         new BindingParameterCollection());     // open the channel listener stack       listenerStack.Open();       // BasicHttp will return (no sessions)       // 2 day timeout from the Binding (ReceiveTimeout)       IReplyChannel receiveChannels = listenerStack.AcceptChannel();     // Open the channel stack (1 day timeout)       receiveChannels.Open();         // receive a request on another thread          // send a message to the receiver       receiveChannels.BeginReceiveRequest(new AsyncCallback(receiveRequest),                                       receiveChannels);       // create the channel factory stack       IChannelFactory<IRequestChannel> channelFactoryStack =         binding.BuildChannelFactory<IRequestChannel>(           new BindingParameterCollection());       // open the channel factory stack       channelFactoryStack.Open();       // create the channel stack from the channel factory stack       // pass a new EndpointAddress to set the target of the Message       IRequestChannel sendChannels = channelFactoryStack.CreateChannel(         new EndpointAddress(address));       // open the channel stack       sendChannels.Open();       // send a request message       Message reply = sendChannels.Request(         Message.CreateMessage(MessageVersion.Soap11, "urn:SomeAction",           "Hi there"));       // show the contents of the reply       PrintHelper.Print(String.Format("Reply received:\n{0}",         reply.ToString()));       // cleanup       sendChannels.Close();       channelFactoryStack.Close();       listenerStack.Close();     }     // the AsyncCallback for BeginReceiveRequest     private static void receiveRequest(IAsyncResult ar) {       // get the channel stack       IReplyChannel channels = (IReplyChannel) ar.AsyncState;       // get the requestContext       RequestContext context = channels.EndReceiveRequest(ar);       // show the received message       PrintHelper.Print(String.Format("Message received:\n{0}",         context.RequestMessage.ToString()));       // create a reply Message       Message reply = Message.CreateMessage(MessageVersion.Soap11,         "urn:SomeReplyAction", "Hi there back");       // send the reply       context.Reply(reply);       // close the context       context.Close();       // close the channels       channels.Close();     } }

The preceding example sends and receives one message. If two messages arrive, the receiving application will not be able to process both. In more-real-world receiving applications, the job of continuing to listen for incoming messages is the job of a set of ServiceModel-layer dispatchers. These dispatchers are covered in Chapter 10, “Dispatchers and Clients.”

Note 

I really enjoy (perhaps this is sad) working with low-level applications like the one we just looked at. I encourage the reader to change the Binding several times and recode the rest of the example. Doing so will, over time, give you a level of comfort with the WCF programming model.




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