by Christoph Herold
27. November 2006 11:28
For the time being, I have built myself a helper class, that tries to fix the "timeout-bug" when using TransactionScope. And since I'm such a nice guy, I decided to let everyone have it ;-)
So here goes, completely commented including a usage example:
using System;
using System.Threading;
using System.Transactions;
namespace HeroldIT.Framework.Utils
{
/// <summary>
/// This class is used to abort transactions when the timeout occurs.
/// </summary>
/// <remarks>
/// <para>This class is required, because TransactionScopes do not abort processing, when they expire. Instead
/// all actions done after the timeout occurs are not included in the transaction, but instead carried out
/// normally. This can lead to inconsistencies in your data, when processing large amounts.</para>
///
/// <para>This class can be used nestedly, i.e. methods called in the supplied working delegate can safely
/// create instances of this class.</para>
/// </remarks>
/// <example><code>
/// try
/// {
/// using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 0, 2)))
/// {
/// new TransactionHelper(delegate() {
/// // do transactive work here
/// });
/// ts.Complete();
/// }
/// }
/// catch (TransactionAbortedException tae)
/// {
/// // handle aborted transaction
/// }
/// </code>
/// </example>
public sealed class TransactionHelper
{
/// <summary>
/// A tagging object to determine, whether the thread is aborted due to a transaction timeout
/// </summary>
private object _abortObject;
/// <summary>
/// The current thread executing the transactive work
/// </summary>
private Thread _currentThread;
/// <summary>
/// Creates a new TransactionHelper that executes transactive statements and aborts the process
/// if a timeout occurs.
/// </summary>
/// <param name="toExecute">The delegate to call for processing</param>
public TransactionHelper(ThreadStart toExecute)
{
if (null == toExecute)
throw new ArgumentNullException("toExecute");
// get current transaction
Transaction tx = Transaction.Current;
if (null == tx)
throw new InvalidOperationException(Resources.ExcNoAmbientTransaction);
// Don't execute, if the transaction is already aborted
if (tx.TransactionInformation.Status != TransactionStatus.Aborted)
{
// keep handle to current thread for aborting
_currentThread = Thread.CurrentThread;
// register completion handler
tx.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted);
// execute transactive code
try
{
toExecute();
}
catch (ThreadAbortException tae)
{
// was the thread aborted by our TransactionCompleted handler?
if (null != _abortObject && tae.ExceptionState == _abortObject)
{
Thread.ResetAbort();
}
}
// unregister completion handler (required when using multiple TxHelpers in one transaction,
// otherwise multiple ThreadAbortExceptions are thrown on timeout)
tx.TransactionCompleted -= new TransactionCompletedEventHandler(Current_TransactionCompleted);
}
}
/// <summary>
/// Transaction completion event handler, that checks, whether a timeout occured
/// and, if so, causes the registered executing thread to abort
/// </summary>
/// <param name="sender">The originating transaction</param>
/// <param name="e">The event args for this event</param>
void Current_TransactionCompleted(object sender, TransactionEventArgs e)
{
// Is the transaction aborted (possibly due to timeout)?
if (e.Transaction.TransactionInformation.Status == TransactionStatus.Aborted)
{
// Is the executing thread registered?
if (null != _currentThread)
{
// generate a new abortion tagging object
_abortObject = new object();
// abort the executing thread
_currentThread.Abort(_abortObject);
}
}
}
}
}
I hope, this helps some of you. Of course, I'll be updating my blog, as soon as some notice of how TransactionScope is supposed to be used reaches me. I know, that ThreadAbortExceptions aren't something to use lightly, but currently I see no other way.
by Christoph Herold
20. November 2006 11:12
One of the biggest pains for web developers is probably the fact, that Microsoft does not support installing multiple versions of IE in one Windows. But there is hope: The folks at tredosoft came up with a nice installer, that allows you to install most versions of IE. There are some small drawbacks, but it should suffice for most tests. You can get it here: http://tredosoft.com/Multiple_IE.
by Christoph Herold
18. November 2006 11:10
Someone has finally found a dirty, yet working solution to the TransactionScope problem. You can register an event on the transaction, that fires, when the transaction finishes, which also happens on a timeout. You can then abort the main thread, to keep further commands from executing.
%lt;p>And this is also the dirty part: ThreadAbortExceptions are a little dangerous and can cause unexpected behavior. But for now it works for me, and as long as Microsoft does not come up with a better solution, I'll have to use this.
You can find more details here: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=876150&SiteID=1