The Service Contract
The service contract for communicating with a MSMQ-hosted queue contains only a single send operation. This is a
limitation specific to the
MsmqIntegrationBinding binding. The single submit operation takes
an
MsmqMessage initialized with the type
ApplicationMessage, which is the message class you created earlier. Also notice that the contract is set to one-way, indicating that no output
message is expected. It is worth noting at this point that
MsmqIntegrationBinding does not
support message-level security. This makes sense because the MSMQ-based service, not being WCF or SOAP aware, would
have no way to interpret the necessary information for message-based security. Transport-level security is supported.
After the contract is created, you can write WCF code directly, open a channel, and submit a message:
MsmqIntegrationBinding binding = new MsmqIntegrationBinding();
binding.Security.Mode = MsmqIntegrationSecurityMode.None;
EndpointAddress address = new EndpointAddress(@"msmq.formatname:DIRECT=OS:.\private$\TestQueue");
ChannelFactory<ISendMessage> channelFactory = new ChannelFactory<ISendMessage>(binding, address);
ISendMessage channel = channelFactory.CreateChannel();
ApplicationMessage message = new ApplicationMessage();
message.Message = "Hello from WCF!";
message.ValidUntil = DateTime.Now;
MsmqMessage<ApplicationMessage> msmqMsg = new MsmqMessage<ApplicationMessage>(message);
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
channel.SubmitMessage(msmqMsg);
scope.Complete();
}
As expected, the
MsmqIntegrationBinding is used. Take special note of the format used for the
endpoint address. This is what actually points WCF to the proper queue. Much like in the legacy MSMQ client, a new
MsmqMessage is initialized with your
ApplicationMessage message class.
The key difference is that instead of using an
MsmqTransaction, a
System.Transactions.TransactionScope is used. You must use a transaction because the queue is
transactional. By default,
MsmqIntegrationBinding sets
TransactionScopeRequired and
TransactionAutoComplete to true.
There is an alternative to the abundance of WCF client code. A service client that extends
ClientBase can be created and the WCF settings can be moved into the configuration file:
public class SendMessageClient : System.ServiceModel.ClientBase<ISendMessage>, ISendMessage
{
public void SubmitMessage(MsmqMessage<ApplicationMessage> message)
{
base.Channel.SubmitMessage(message);
}
public void SubmitMessage(ApplicationMessage message)
{
MsmqMessage<ApplicationMessage> msmqMsg = new MsmqMessage<ApplicationMessage>(message);
SubmitMessage(msmqMsg);
}
}
Notice the additional overload of
SubmitMessage. This new overload, while not required,
allows the caller to use the WCF/MSMQ service without having to actually construct
a
MsmqMessage, further isolating it from the specific implementation details. The caller
simply needs to instantiate an
ApplicationMessage and the WCF client handles packaging it up
into a
MsmqMessage. The caller need never know that the ultimate destination for the
message is a MSMQ queue.
The final step when extending ClientBase is moving the configuration out of code and into the
application configuration file (see Listing 1).
Depending on the needs of the developer, either method (code or configuration based) can be used to create the WCF client. The MSMQ-based service application requires no changes. The advantage to this approach is that the client application is completely isolated from the details of the service. It has knowledge only of the contract. If, at some point, the MSMQ-based service were to be replaced with a WCF-based service, only the WCF-client configuration would need to change. The client application itself would remain the same. At this point, you have successfully isolated the things that change from the things that do not.
The next article in the series will explore how the MSMQ-based service created here can be transformed into a WCF-based service that uses queues.