MsmqActivities v1.1 Released

I’ve just uploaded a new release (v1.1) of my MSMQ Activities for Windows Workflow Foundation. This release does a few general fixes and polishes a few things here and there, but the largest change is a modification of the MsmqListenerService runtime service used by the MsmqReceiveActivity to listen to the MSMQ queues. This change is aimed at improving the situation I talked about on this past entry. Basically, this is something that will be very handy to ensure the activities work even when the hosting process dies unexpectedly and is restarted.

As part of this release, a new interface is introduced in the component: IMsmqSubscriptionPersistenceService (sorry for the long name). This interface is used by the MsmqListenerService to handle persistence of all subscriptions created at runtime to a given MSMQ Queue. This interface has three methods that are consumed by the listener service:

  • Persist(string host, MsmqSubscription subscription):
    This method should take the specified subscription and save it to a persistent storage, and is called when an MsmqReceiveActivity subscribes to a given MSMQ Queue.
    This method is called inside a System.Transactions.TransactionScope so that the listener service can abort the persistence if anything happens after that.
  • IEnumerable<MsmqSubscription> LoadAll(string host):
    This method is called when the listener service is started (right after the WorkflowEngine is started on the process, in fact) to get a list of all subscriptions stored for the given host. For each subscription that is loaded, the listener service configures again a listener on the queue the subscription relates to.
  • Remove(MsmqSubscription subscription):
    This method is called when a MsmqReceiveActivity removes a subscription to a queue. It should delete any record kept about the specified subscription in the persistent storage.

The host argument to Persist() and LoadAll() is a very basic mechanism to allow you to keep a single persistent storage for subscriptions coming from multiple applications (i.e. multiple processes/machines hosting the WorkflowRuntime). The host name to use is specified when you create the MsmqListenerService instance either through an argument in a new constructor overload, or through the configuration file through the “hostname” attribute. If you do not specify a hostname, the value “default” will be used instead. Be careful to always set the hostname explicitly if you’re going to be sharing the persistence storage.

Out of the box, I provide two implementations of the IMsmqSubscriptionPersistenceService interface:

  • NullMsmqSusbcriptionPersistenceService: This is an empty implementation that doesn’t do anything. It can be used if you simply don’t want or need to support subscription persistence.
  • SqlMsmqSubscriptionPersistenceService: This implementation stores all subscriptions as records in a table in a SQL Server database called “MsmqSubscriptions”.

You can select which implementation to use by simply adding it to the WorkflowRuntime’s runtime services collection (either through code or through the configuration file). In the case of the SQL implementation, you’ll need to provide the connection string to the database to use either through the constructor or through the “ConnectionString” attribute in the configuration XML.

Notice that you because of this, you could easily plug in your own custom persistence implementation to support other databases or repositories. One cool thing is that the WorkflowRuntime in essence acts here as an Inversion of Control (IoC) implementation for anything running on the Windows Workflow Foundation platform!

Comments (9)

Jon FlandersOctober 20th, 2006 at 8:33 pm

Hey Tomas – sorry I missed your earlier entry – but why not just have your service hook into the WorkflowLoaded event? That way if a Workflow came up from the persistence store and you didn’t have a listener – you could recreate it from the Workflow definition (e.WorkflowInstance.GetWorkflowDefinition()).

Tomas RestrepoOctober 20th, 2006 at 8:44 pm

Jon,
I thought about doing something similar, but I was under the impression that wouldn’t quite work. Here’s what I was thinking:
If the workflow is "stuck" waiting for the MSMQ message to arrive at the queue, and the host is unloaded, then when the host was reloaded the workflow instance wouldn’t have been reloaded by the engine because it was idled and not running (at least that’s what I understood from our earlier discussion in the forums). If so, hooking into the WorkflowLoaded event wouldn’t do me any good as it is precisely my MsmqListenerService the one would be responsible for "waking" the workflow.
Besides of this, I don’t see how hooking into the WorkflowLoaded event would’ve saved me the trouble of keeping track of the active subscriptions in a persistent store. Maybe I’m missing something, in which case I’d sure appreciate a hint :)

Jon FlandersOctober 20th, 2006 at 10:00 pm

I think you are correct that handling WorkflowLoaded won’t work in this case -sorry I wasn’t thinking it throught.
I am still concerned about your approach (essentially another persistence service to take care of). Especially in light of:
a) How will this work in a mulit-host instance environment
b) How will it stay consistent with the state of the workflow itself (what if your service gets out of sync with the current state of the workflow).
Which is why I would prefer some system that used the metadata of the activity instance itself – I would lean toward SqlPersistenceService.GetAllWorkflows – which would allow you to get the activity metadata without having to load it into memory. OTOH – this is only on the OOB persistence service.

DubMarch 15th, 2007 at 4:36 am

bug (SqlMsmqSubscriptionPersistenceService.cs):
public SqlMsmqSubscriptionPersistenceService(string connString)
{
if ( String.IsNullOrEmpty(_connectionString) )
throw new ArgumentNullException("connString");
_connectionString = connString;
}

Tomas RestrepoMarch 15th, 2007 at 4:25 pm

Dub,
Absolutely right. I’ve fixed it and updated the download. Thanks!

LarryMay 11th, 2009 at 3:23 pm

Hi Tomas,

It appears this handles the situation where we would need to have the context information like is supplied with the wsHttpContext and other context related bindings. Although I can write a custom binding for the ReceiveActivity to connect to MSMQ, MSMQ does not have the context information.

How are you getting the instance ID?

Tomas RestrepoMay 11th, 2009 at 5:35 pm

Larry: Not sure I understand the question. Are you referring to the Workflow Instance ID stored in the subscription db?

LarryMay 12th, 2009 at 4:06 pm

Yes, I am referring to the workflow instance ID.

Tomas RestrepoMay 12th, 2009 at 5:25 pm

Larry: If you look at the code, the workflow instance id is obtained in the runtime service (MsmqListenerService), in the Subscribe() method simply by using WorkflowEnvironment.WorkflowInstanceId.

The Subscribe() method is called by the MsmqReceiveActivity from its implementation of IEventActivity.Subscribe()

Leave a comment

Your comment