COM+ like Transactions

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);
	}

}