I’m implementing a component that offers COM+ like programming model on local transaction on a single database. You will be able to decorate a class using attribute like [Transactional(TransactionOption, TransactionIsolation)]. Inside each method, SetComplete or SetAbort will vote to commit or rollback the transactions. It will support nested transactions on a single thread.
The motivation is to ease business component development that needs flexible transaction management when component reuse is important. Here’s a sample test case.
[Transactional(TransactionOption.Required, TransactionIsolation.Standard)] public class Account : Transactional { public Account() : base() { } public void Debt(string account, decimal amount) { try { // make database calls in adapter via ADO.NET Auditor auditor = new Auditor(); DA.PostDebt(account, amount); auditor.Log("post debt trx"); TransactionUtil.SetComplete(); } catch (Exception ex) { auditor.Log("error in posting debt trx"); TransactionUtil.SetAbort(); } } public void Credit(string account, decimal amount) { try { // make database calls in adapter via ADO.NET DA.PostCredit(account, amount); Auditor auditor = new Auditor(); auditor.Log("post credit trx"); TransactionUtil.SetComplete(); } catch (Exception ex) { auditor.Log("error in posting debt trx"); TransactionUtil.SetAbort(); } } } [Transactional(TransactionOption.RequiresNew, TransactionIsolation.Standard)] public class TransferAgent : Transactional { public TransferAgent() : base() { } public void Transfer(string from, string to, decimal amount) { try { Auditor auditor = new Auditor(); auditor.Log("validating balance in " + from); Validator v = new Validator(); v.Validate(from, amount); auditor.Log(from + " balance checked OK"); Account account = new Account(); account.Debt(from, amount); account.Credit(to, amount); TransactionUtil.SetComplete(); auditor.Log("transfer OK"); } catch (Exception ex) { auditor.Log("error in transfer"); TransactionUtil.SetAbort(); } } } [Transactional(TransactionOption.RequiresNew, TransactionIsolation.Standard)] public class Auditor : Transactional { public Auditor() : base() { } public void Log(string msg) { try { DA.Log(msg); TransactionUtil.SetComplete(); } catch (Exception ex) { TransactionUtil.SetAbort(); } } } [Transactional(TransactionOption.Supported, TransactionIsolation.Standard)] public class Validator : Transactional { public Validator() : base() { } public void Validate(string account, decimal balance) { try { if (DA.CheckBalance(account, balance)) TransactionUtil.SetComplete(); else TransactionUtil.SetAbort(); } catch (Exception ex) { TransactionUtil.SetAbort(); } } } [TestFixture] public class UnitTest : UnitTestBase { [Test] public void Test01() { Account account = new Account(); account.Credit("a", 1000.00); account.Debt("a", 100); account.Credit("b", 100.00); } [Test] public void Test02() { TransferAgent agent = new TransferAgent(); agent.Transfer("a", "b", 100.00); } }