Question

I was able to create a test application using Rhino Service Bus and Sql Server Express. I have an MVC 4 front end that creates a message and sends it to a queue and also creates a record in my database table. I have a windows service that grabs the messages from the queue and writes to a separate database table. I want the message send/database write to be all or nothing. For example, if either the message send or the database write fail, I want to be able to roll them both back. I tried wrapping them both in a TransactionScope, but based on my tests, the messages get sent to the queue even without the scope.Complete being called.

Is this possible? If so, can someone point me in the right direction?

FYI...the queues are MSMQ transactional queues

Thanks.

Was it helpful?

Solution

I posted the same issue on the Rhino Service Bus Google Groups site and got a response from Oren. It turns out that RSB will work with transactions, but I was using a IOnewayBus, which does not use DTC. I made the change to use a IStartableServiceBus and everything now works as I expected.

OTHER TIPS

We had a problem with Rhino Service bus whereby implicity all transactions get enlisted into an ambient transaction (MSDTC). This was ok for a while but we run a number of long running processes that will take longer than an hour (DTC has maximum timeout of one hour). In order to get around this problem I created an implementation of IMessageModule as below:

/// <summary>
/// alows commands to opt-in to ambient transaction (MSDTC)
/// </summary>
/// <remarks>will supress by default</remarks>
public class SuppressAmbientTransactionMessageModule : IMessageModule
{

    [ThreadStatic]
    private static TransactionScope transactionScope;

    public void Init(ITransport transport, IServiceBus serviceBus)
    {
        transport.MessageArrived += TransportOnMessageArrived;
        transport.BeforeMessageTransactionCommit += transport_BeforeMessageTransactionCommit;
        transport.BeforeMessageTransactionRollback += transport_BeforeMessageTransactionCommit;
    }

    public void transport_BeforeMessageTransactionCommit(CurrentMessageInformation currentMessageInformation)
    {
        if (!(currentMessageInformation.Message is IWithAmbientTransaction))
        {
            transactionScope.Dispose();
        }
    }

    public void Stop(ITransport transport, IServiceBus bus)
    {
        transport.MessageArrived -= TransportOnMessageArrived;
        transport.BeforeMessageTransactionCommit -= transport_BeforeMessageTransactionCommit;
        transport.BeforeMessageTransactionRollback -= transport_BeforeMessageTransactionCommit;
    }

    public bool TransportOnMessageArrived(CurrentMessageInformation currentMessageInformation)
    {
        if (!(currentMessageInformation.Message is IWithAmbientTransaction))

        {
            transactionScope = new TransactionScope(TransactionScopeOption.Suppress);
        }
        return false;
    }
}

I also created a methodless interface called IWithAmbientTransaction to enable specific command to enlist in an ambient transaction. WHile its not ideal to have such long running process thsis approach has solved our immediate problems of long running transactions

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top