A Weekend With Indigo. Part 1: Simple Messaging
This weekend I will create some samples for myself as a foundation for learning and fiddling around with Indigo bindings. A “binding” is a combination of transport and behavior settings that binds a service contract to an endpoint and it is a conceptual and functional superset of what wsdl:binding does. One of the great things about Indigo is that changing bindings and therefore adding/removing capabilities and even adding/exchanging/removing transports can be done with no impact on the code itself. All of that can be done in configuration. So what I will do is to share the base samples with you as I write them, explain a couple of concepts along the way, and use some very simplistic bindings for starters. Once I have figured out how the bindings stuff works (Citing one of the Indigettes at Microsoft: “That’s the part of the product that I’m afraid will be rocket science”), I can later reference these samples and show using configuration snippets what behaviors (e.g. transactions, security, reliable messaging) can be used in combination with which transports and contract types. So, watch this space, you can expect some code here.
What I’ll start with is the most simplistic and “raw” way to use Indigo that’s practical for someone with a life. The extensibility model will let you reach even deeper down into the guts, but I don’t want to drag you down there too far. Also, I don’t really know all of the scary dragons lurking there is the dark. I also want to start with this example to dispel some people’s impression that Indigo is just another “square brackets RPC-ish thing”.
The snippet below is likely the simplest possible Indigo service. I define an IGenericMessageEndpoint contract that has a single operation Receive, which expects an System.ServiceModel.Message as input. The Message class is the immediate representation of a message that the entire Indigo infrastructure uses internally and that can be surfaced to the application using a contract definition like this. The [OperationContract] attribute signals that the operation is “one way”, so it’s clear that we’re not sending any immediate responses and not even faults. The Action is set to “*”, which is a wildcard indicator specifying that all messages, irrespective of their Action URI will de dispatched here. That is, unless there would be another operation without a wildcard Action. In that case, all messages matching the concrete Action URI of that operation would be dispatched there and all other messages would flow into the wildcard operation.
The implementation of the contract in GenericMessageEndpoint just dumps the content of the message body onto the console by acquiring the XmlDictionaryReader of the message and writing the string’ized body content out.
The Server class constructs a ServiceHost<GenericMessageEndpoint> for the service implementation, which constructs endpoints from configuration settings, hosts these endpoints, and is responsible for creating instances of the service as messages arrive and need to be dispatched. As you can see, I do nothing more than constructing the host and Open it. The specifics of what transport is used and where the service is listening will be supplied in config, as you’ll see further down.
using System; |
Below is the matching “raw” client. What we want to do here is to just construct a System.ServiceModel.Message, put some XML into it and throw it over the fence. To do that, I construct a client side contract IGenericMessageChannel (I am doing that to show that we’re really in “contract-free” raw messaging territory here) that has a Send operation, which “looks right” on the sender side vs. the receiver contract’s Receive, and also flags the operation as one-way and with a wildcard Action.
To setup a channel to the destination service, I can now (in SendLoop) construct a ChannelFactory<IGenericMessageChannel> over that contract and with the argument “clientChannel”, which is a reference into the configuration as I’ll show in a little bit. The channel factory is the client-side counterpart of the service host. It reads all information about the channel from the configuration, evaluates the bindings, binds to the right transports and behaviors, and also knows about the endpoint to talk to. Once I have a channel factory, I can Open it and have it give me a channel (or “proxy”) that I can talk through. In SendMessage I cook up a Message from an Action URI that I make up and an XmlReader instance layered over an XmlDocument that I keep around and send that out to the service.
using System; |
The surrounding application for Client and Server (I run both in the same process for simplicity) is, of course, trivial. All I do is to construct and start the server, construct a client and call its send loop and then wait for the user to be amazed of the (server’s) console output and have him/her press ENTER to quit. If I were making this more elaborate, I could wait until all sent messages had arrived at the service side and shut down automatically, but this is supposed to be simple.
using System; |
That’s as much code as we need to implement a one-way messaging client/server “system” that can throw XML snippets across a network transport.
To make it work, we need to configure this application and “deploy” it to a concrete environment. A simple configuration (assuming this is all compiled into “SimpleMessaging.exe” and hence the assembly name is “SimpleMessaging”) could look like the one shown below.
The <bindings> section contains one <customBinding> (means: I am not anything predefined), with a concrete configuration named “defaultBinding” that uses the tcpTransport. If I were setting up security or reliable messaging, would also be doing that here and add the respective config elements alongside the TCP transport binding element, but we will keep it simple for the time being.
The <client> section defines, for the configurationName=”clientChannel” (look above in the client snippet how that maps to the ChannelFactory< IGenericMessageChannel > constructor call), which binding should be used. The example links up to the customBinding type and within that type to the “defaultBinding” config. Furthermore, the section defines to which contract type ([ServiceContract]-labeled interface or class) the endpoint is bound and, lastly and most importantly, the address at which the endpoint is listening to client messages.
The <service> section defines the server side of the story. The association between the service host and the configuration is done via the serviceType attribute. When the ServiceHost<GenericMessageEndpoint> is contructed in the server snippet above, the service host locates the section for the respective service type it is hosting by looking at this attribute. The endpoint definition on the server side is very similar to the client side, which should not be very surprising. It also refers to a binding using bindingType/bindingConfiguration, defines the address at which the service will be listening, and indicates which contract type applies for the endpoint.
<?xml version="1.0" encoding="utf-8" ?> |
Running it all yields the following output, spit out by the server side, and just as expected:
<rose>is
a</rose> |
Very simple and versatile one-way messaging flowing free-form XML. Not at all RPC-ish.