The normal programming model in WCF relies on addresses, bindings, and contracts. Nowhere in that programming model is there mention of channels or channel managers. As you’ve seen in previous chapters, channels and channel managers do real messaging work, but working directly with these types is prohibitive in most environments. Instead of being part of the normal programming model, channels and channel managers are a vital part of the flexibility needed for current and future messaging requirements. This includes the transports, protocols, and message encodings required in an application, as well as the ones that will undoubtedly arise in the future. The ServiceModel layer serves to manage the lifetime of these channel layer constructs, provide higher-level functionality not suited to the channel layer (like service instancing and message filtering), and expose to the developer an easy-to-use developer application programming interface (API).
Before we delve into the anatomy of the client and the dispatcher, let’s spend some time examining the issues that we would need to take into account if we rely only on the channel layer. Consider the following application, which sends itself a message using the messaging infrastructure created by the BasicHttpBinding:
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(); // create an address Uri address = new Uri("http://localhost:4000/MyListener"); // build the ChannelListener stack IChannelListener<IReplyChannel> listenerStack = binding.BuildChannelListener<IReplyChannel>(address, new BindingParameterCollection()); // Open the listener stack listenerStack.Open(); // Create the Channel stack IReplyChannel receiveChannels = listenerStack.AcceptChannel(); // Open the channel stack receiveChannels.Open(); // Try to Receive a Message, need to do async receiveChannels.BeginReceiveRequest( new AsyncCallback(receiveRequest), receiveChannels); // build 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 IRequestChannel sendChannels = channelFactoryStack.CreateChannel(new EndpointAddress(address)); // open the channel stack sendChannels.Open(); // send a message to the receiver Message reply = sendChannels.Request(Message.CreateMessage(MessageVersion.Soap11, "urn:SomeAction", "Hi there")); // show the contents of the reply Console.WriteLine("\nReply Received:\n{0}", reply.ToString()); // cleanup sendChannels.Close(); channelFactoryStack.Close(); listenerStack.Close(); } // invoked when a message is received 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 Console.WriteLine("\nRequest Received:\n{0}", context.RequestMessage.ToString()); // create a reply 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(); } }
Most of these lines of code are devoted to creating and managing the lifetime of the channel managers and channels required to send and receive a message. Even with all of this code, this application is limited in its functionality. For example, we can send and receive only one message; adding support for additional transports, protocols, and encodings requires much more code; the sender and receiver have no way to expose a contract via Web Services Description Language (WSDL) and Extensible Schema Definition (XSD); and so on. Adding this sort of functionality manually is a daunting task. Among their other roles, the dispatcher and the client automate this work, thereby allowing us to focus on the functionality of our application rather than the infrastructure.