Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / Visual Basic
Article

Programming MSMQ in .NET - Part 2: Transactional Messaging

Rate me:
Please Sign up or sign in to vote.
4.38/5 (29 votes)
15 Jun 20038 min read 262.1K   2.2K   91   33
This article talks about transactional messaging with MSMQ and the implementation aspects wrt .NET

Introduction

In the previous article, we saw some very basics of MSMQ programming like creating a queue, sending and receiving simple messages etc. In this article, we shall consider an important aspect of programming MSMQ: Transactional messaging. We shall start off by understanding why you need transactional messaging, what it is,  followed by some simple code snippets showing how it can be done in .NET. Finally, we shall wrap up the article considering two case studies where transactional messaging with MSMQ has been applied.

Why Transactional Messaging?

Before plunging into transactional messasing, let us consider some problems with non-transactional messaging.

Messages can be lost

By default, MSMQ stores messages in memory when they are sent, received or routed across different servers. This is done only to boost performance as persisting messages on the disk is an extra overhead. So, if the MSMQ service crashes or the machine holding the messages goes down unexpectedly, then messages are bound to get lost.

As a work around to this problem, we can set the Recoverable property of the message to True. This property can also be set at the queue level by setting the DefaultPropertiesToSend property. The  code snippet shown below indicates the same.

VB.NET
Dim queue As New MessageQueue() 
queue.Path = ".\Private$\TestQueue" 
queue.DefaultPropertiesToSend.Recoverable = True 

'This method can also be used 
'Here, we use the Recoverable property of the message itself 
'Dim msg As New Message() 
'msg.Body = "My recoverable message" 
'msg.Label = "Some Label" 
'msg.Recoverable = True 
'queue.Send(msg) 

queue.Send("My recoverable message","Some Label") 
queue.Close()

Duplicate messages

In the above implementation, we solved the problem of messages being lost in transit. But still, this does not guarantee that the messages aren't duplicated. It is definitely not acceptable for a reliable application to process a message twice as it is to have lost messages.

Both the above problems are successfully solved by the usage of transactional messaging. Now, you may ask me, considering the above explanation, we may always end up using transactional messaging. Now, when do we actually use non transactional messaging and why? The answer to this question is simple. If reliability is not a concern for your application, that is, if your application is not affected by lost messages or the order of arrival of messages, use non transactional messaging. Remeber that the reliability offerered by transactional messaging comes at a cost: performance.

One rough analogy that immediately comes to my mind is usage of TCP as against the usage of UDP. UDP is analogous to non transactional messaging. UDP does not ensure that the packets arrive to the destination and they do in the same order. TCP on the other hand, ensures that the packets reach the destination and they arrive in the same order as they were sent. Therefore, TCP is analogous to non transactional messaging. Likewise, the choice between TCP and UDP is purely made based on the level of reliability and performance required for the networking application.

Creating a Transactional Queue

To implement transactional messaging, you would require to create a transactional queue first. This can be done either through the computer management console snap-in or programmatically.

Through the snap-in

Creating a transactional queue

Fig 1: Creating a transactional queue

Transactional queues are created just like normal queues. We just have to check the Transactional check box as shown in Fig 1.

Programmatically

To create a transactional queue programmatically, we need to use one of the overloads of Create method of MessageQueue class which takes a boolean parameter indicating whether the queue to be created is transactional or not. We should pass True here. For eg.

VB.NET
MessageQueue.Create("pc-manoj\Private$\MyTranQueue", True)

Note: The Transactional attribute of a queue cannot be changed after the queue is created.

Understanding Transactional Messaging

MSMQ supports two types of transactions: Internal and External. Let's have a look at them one at a time.

Internal Transactions

MSMQ facilitates sending and receiving multiple messages in a transaction through the MessageQueueTransaction class. The Send and Receive methods of the MessageQueue class have overloads that accept an instance of the MessageQueueTransaction class to indicate that the operation falls under a transaction. The transaction itself can be controlled using the Begin method (which starts a transaction) , Commit and Abort methods (which commits and rollbacks the transaction respectively). Let's consider an example to illustrate their usage:

VB.NET
Dim mqTran As New MessageQueueTransaction()

Dim queueA As New MessageQueue()
queueA.Path = ".\Private$\TranQueueA"
Dim queueB As New MessageQueue()
queueB.Path = ".\Private$\TranQueueB"

mqTran.Begin()
Try
    queueA.Send("Message A", "Label A", mqTran)
    queueB.Send("Message B", "Label B", mqTran)
    mqTran.Commit()

Catch ex As Exception
    mqTran.Abort()
Finally
    queueA.Close()
    queueB.Close()
End Try

In the above code snippet, we are sending two messages to two different queues in the same transaction.

Many a times, you may want to query on the status of a transaction and then take some action based on the status. The MessageQueueTransaction class exposes a Status property which gives you just that. It is exposed as an enumeration whose values are described below:

ValueDescription
InitializedThe transaction has been initialized but not yet started. When a MessageQueueTransaction class is just instantiated, this will be the status.
PendingThe transaction has not yet been committed or aborted. This will be the status when you begin a transaction using Begin method but not have committed or rolled back (called Commit or Abort methods).
CommittedThe transaction has been committed.
AbortedThe transaction has been aborted.

Using the MessageQueueTransactionType enumeration

The Send and Receive methods of the MessageQueue class  also expose overloads which takes a parameter of type MessageQueueTransactionType which is an enumeration of certain values.  This basically specifies how you would like to interact with the queue (transactionally). We have not used it thus far, but is important to understand why and where this is used.

The enumeration contains three values:

Single

You might often come across situations where you want to have each queue operation in a separate internal transaction. That is, you may not want to use MessageQueueTransaction object to qualify each MSMQ operation. In such cases you can use the Single option. Here's an example:

VB.NET
Dim queueA As New MessageQueue()
queueA.Path = ".\Private$\TranQueueA"
queueA.Send("Single internal transactional message A", "Label A", _
    MessageQueueTransactionType.Single)
queueA.Close()

Understand that, to send a message to a transactional queue, you have to be in a transaction (internal or external), else an error will be thrown.

None

Using this option enables us to receive a message from a transactional queue, but outside a transaction. We can also use this option to send a transactional message to a non transactional queue. 

Automatic

This option is used in conjunction with external transactions. This directs the send or receive operation to use an already existing transaction context created by COM+, MTS etc. This is shown in the next section.

External Transactions

The above section dealt with doing multiple MSMQ operations within the same transaction. But, in a distributed application it is many a time required to include MSMQ operations as a part of an other transaction. For example, you may want to receive a message from a queue followed by doing some database operations; like updating a table etc. To cater to these needs, MSMQ supports DTC based transactions. Like other resource managers like Oracle and SQL Server, MSMQ can be enlisted in a distributed transaction. For example, in the below code snippet, I am doing an insert into a database table followed by a sending a message to a queue.

VB.NET
Dim conn As New SqlConnection( _
    "server=.;database=pubs;Trusted_connection=yes")
Dim cmd As New SqlCommand( _
    "Insert into Orders Values('OrderA','Manoj')", conn)
Dim queueA As New MessageQueue()

Try
    conn.Open()
    cmd.ExecuteNonQuery()

    queueA.Path = ".\Private$\TranQueueA"
    queueA.Send("OrderA for Manoj", "Order A", _ 
      MessageQueueTransactionType.Automatic)

    ContextUtil.SetComplete()
    Return True
Catch ex As Exception
    ContextUtil.SetAbort()
    Return False

Finally
    conn.Close()
    queueA.Close()
End Try

One thing to understand is that, when transactional messaging happens (internal or external), a message is not written to the queue until the transaction is committed. Likewise, if a message is received in a transaction, it is read right away, but is replaced back to the queue if the transaction aborts.

Also, be clear as to when, what type of transactional messaging should be used. External transactions are much slower that Internal transactions because of the overhead involved with respect to DTC. If you deal only MSMQ operations in your transaction, use Internal transactions as they are more efficient. Use External transactions if your transaction involves more than one resource manager like a database etc.

Case Study

Now, let's consider two cases where transactional messaging has been used. With these examples, the idea behind transactional messaging should become crystal clear.

Purchase Order Application

Purchase Order application

Fig 2 : A Purchase Order application

Given above is a very simplified diagram of a purchase order application. The application has two parts: One which places customer orders and the other which processes it. When a customer places an order, they are not processed immediately, but are processed at a later point during the day. The order processing application processes these orders in the same order they were placed and then confirms or rejects the delivery based on some conditions. The application has been implemented using MSMQ as the transport mechanism. Every time an order is placed by the customer, a message describing the same is placed on a queue and the processing application picks up these messages and processes them later. Now here, we would need transactional messaging because we need to ensure that:

  • The placed orders are not lost.
  • The orders are processed in the same order they were placed.

Note that we can not ensure the above requirements with non-transactional messaging.

Queued Components (QC)

Queued components are a service provided by COM+ that lets you call components asynchronously. The architecture of queued components is based on transactional messaging provided by MSMQ. I will not delve in depths of QC but will just tell very briefly how QC works.

When a queued component is instantiated  by the caller, the queued component itself is not actually instantiated. Instead, a component called QC recorder is instantiated. When method calls are made on the queued component, the recorder packages all these calls into an MSMQ message and posts it to a transactional queue. A listening application picks up this message from the queue and plays the message.  By playing, I mean instantiating the actual component and then calling all the methods in the order they were made by the caller.

So, by using transactional messaging, what has been ensured here is that the queued component is definitely called (the message is not lost) and the method calls are made only once (no duplication of messages). Therefore, though the calls are asynchronous, the queued component calls are a part of a logical transaction.

Conclusion

By now, you should have, hopefully, understood the idea of transactional messaging and where and how they can be applied. I am not done with MSMQ yet :). In the next article, we shall consider some other interesting aspects of MSMQ programming like Message Formatters, Co-related messaging and so on.

 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
India India
I am a software developer and have worked on Microsoft technologies for about five years now. I have always been fascinated by Microsoft technologies and with the advent of .NET , this fascination has reached new heights. I take a lot of interest reading technical articles and equally enjoy writing them. I really like to be called a .NET junkie and will surely try to live up to this name Smile | :)

I am .NET MVP and have also completed MCAD, MCSD(VS 6), MCDBA (SQL Server 2000), MCSA (Win 2K) and MCTS (Distributed Apps) certfications.

Comments and Discussions

 
GeneralMSMQ in Distributed Application. Pin
dotnetcoders8-Aug-05 21:38
dotnetcoders8-Aug-05 21:38 
GeneralMessaging Between non-Transactional and Transactional Queues Pin
Morrislgn1-Dec-04 5:25
Morrislgn1-Dec-04 5:25 
Generalasdf Pin
Anonymous8-Nov-04 23:31
Anonymous8-Nov-04 23:31 
GeneralMSMQ in disconnected environment Pin
ashish mathur27-Jan-04 15:32
ashish mathur27-Jan-04 15:32 
GeneralQueued Components (VB.NET) Pin
Thomas357724-Jul-03 19:49
Thomas357724-Jul-03 19:49 
GeneralRe: Queued Components (VB.NET) Pin
Manoj G25-Jul-03 23:34
Manoj G25-Jul-03 23:34 
GeneralA Question Expanding on This Pin
CedricBertolasio9-Jul-03 4:32
CedricBertolasio9-Jul-03 4:32 
GeneralRe: A Question Expanding on This Pin
Manoj G9-Jul-03 17:27
Manoj G9-Jul-03 17:27 
Hi,

Not a problem. Make sure the queue u use is transactional. So if the transaction involving MSMQ fails, the message goes back to the queue again. So, you can start your transaction afresh later.

HTH,


Manoj G [.NET MVP]
GeneralRe: A Question Expanding on This Pin
CedricBertolasio9-Jul-03 18:54
CedricBertolasio9-Jul-03 18:54 
GeneralRe: A Question Expanding on This Pin
Manoj G10-Jul-03 4:29
Manoj G10-Jul-03 4:29 
GeneralRe: A Question Expanding on This Pin
CedricBertolasio10-Jul-03 4:46
CedricBertolasio10-Jul-03 4:46 
GeneralRe: A Question Expanding on This Pin
Stan4th5-Jul-05 5:43
Stan4th5-Jul-05 5:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.