Ahead of the horror of tomorrow…here’s a little class I was playing with as a demo, let me know what you think (it’s partially based on this example)
using System;
using System.Collections.Generic;
using System.Text;
using System.Transactions;
using System.Collections;
namespace TransTest
{
public class TransactedHashtable : Hashtable, IEnlistmentNotification, ISinglePhaseNotification, IPromotableSinglePhaseNotification
{
Transaction transaction;
Hashtable holdingTable;
public TransactedHashtable()
{
Enlist();
holdingTable = new Hashtable();
}
void Enlist()
{
transaction = Transaction.Current;
if (transaction != null)
transaction.EnlistVolatile(this, EnlistmentOptions.None);
}
public override void Add(object key, object value)
{
if (transaction == null || transaction != Transaction.Current) Enlist();
holdingTable.Add(key, value);
}
void OnCommit()
{
foreach (DictionaryEntry de in holdingTable)
{
base[de.Value] = de.Value;
}
holdingTable.Clear();
}
#region IDisposable Members
public void Dispose()
{
base.Clear();
}
#endregion
#region IEnlistmentNotification Members
public void Commit(Enlistment enlistment)
{
OnCommit();
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
//preparingEnlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
holdingTable.Clear();
}
#endregion
#region ISinglePhaseNotification Members
public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
{
OnCommit();
singlePhaseEnlistment.Committed();
}
#endregion
#region IPromotableSinglePhaseNotification Members
public void Initialize()
{
}
public Transaction Promote()
{
return transaction.Clone();
}
public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
{
this.holdingTable.Clear();
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Transactions;
using System.Collections;
using System.Data.SqlClient;
namespace TransTest
{
class Program
{
static void Main(string[] args)
{
TransactedHashtable t = new TransactedHashtable();
using(TransactionScope ts = new TransactionScope())
{
using (SqlConnection conn = new SqlConnection("database=Northwind;data source=127.0.0.1;Integrated Security=SSPI;"))
{
conn.Open();
SqlCommand comm = new SqlCommand("UPDATE Customers SET City = 'New York' WHERE (CustomerID = 'ALFKI')");
comm.Connection = conn;
comm.ExecuteNonQuery();
}
t.Add("aaaaa", "aaa");
t.Add("bbbbb", "bbb");
ts.Complete();
}
foreach(DictionaryEntry de in t )
{
Console.WriteLine(de.Key.ToString() + " :: " + de.Value.ToString());
}
using (TransactionScope ts = new TransactionScope())
{
using (SqlConnection conn = new SqlConnection("database=Northwind;data source=127.0.0.1;Integrated Security=SSPI;"))
{
conn.Open();
SqlCommand comm = new SqlCommand("UPDATE Customers SET City = 'Berlin' WHERE (CustomerID = 'ALFKI')");
comm.Connection = conn;
comm.ExecuteNonQuery();
}
t.Add("ccccc", "ccc");
t.Add("ddddd", "ddd");
//ts.Complete();
}
foreach (DictionaryEntry de in t)
{
Console.WriteLine(de.Key.ToString() + " :: " + de.Value.ToString());
}
Console.ReadLine();
}
}
}
Since the second add block doesn’t commit the transaction, it’s not committed (and rolled back…which just means I don’t add them to the hashtable in this instance). Anyway, I thought it was a nice little example…opinions???
UPDATE: Very much a work in progress (since there's virtually no documentation on how to do this yet), just updated this to support promotion, the above example just uses the SQL connections to force promotion to the OLETx Manager (using MSDTC)...suspect this will become pretty common in the years to come :0)