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