OpenAccess relies on lazy loading and change tracking to make is as easy as possible for the user to work with persistent data. But we need a point in time where we can collect all changes in memory and execute the insert, update and delete statements to persist the changes. Typically this is the job of a transaction.

The easiest way is to never let OpenAccess run without a started transaction. In this case you only have to call commit or rollback.

 

IObjectScope scope = Database.Get("DatabaseConnection1").GetObjectScope(); 
scope.TransactionProperties.AutomaticBegin = true// transaction is always running! 
Person p = new Person { Name = "Jan" }; 
scope.Add(p); 
scope.Transaction.Commit(); 
p.Name = "Changed"
scope.Transaction.Rollback(); 
 

 

As you can see with one line of code executed after you finished your business operation everything is persisted. With this setup you lose one OpenAccess feature, if no transaction is started the ObjectScope is in read-only mode. Read only scopes help you to find write access to persistent classes where it is not allowed in your work flow. If a use case is only reading you should not start a transaction, if a use case has the allowance to write just start a transaction.

 

IObjectScope scope = Database.Get("DatabaseConnection1").GetObjectScope(); 
Person p = new Person { Name = "Jan" }; 
scope.Transaction.Begin(); 
scope.Add(p); 
scope.Transaction.Commit(); 
p.Name = "Changed"// exception 
 

 

Microsoft .NET comes with a build-in transaction framework. OpenAccess supports this framework as well and it is the way to integrate OpenAccess into distributed transactions. The integration can be used in explicit or implicit mode. The implicit mode will register OpenAccess in a running transaction when the first write operation on persistent data is executed.

 

Database db = Database.Get("DatabaseConnection1"); 
db.Properties.TransactionProvider = TransactionProvider.TransactionScope; 
IObjectScope scope = db.GetObjectScope(); 
Person p = new Person { Name = "Jan" }; 
using (TransactionScope txn = new TransactionScope()) 
    scope.Add(p); 
    txn.Complete(); 
// commit 
using (TransactionScope txn = new TransactionScope()) 
    p.Name = "Changed"
}// rollback 
 

 

The explicit mode needs a registration of OpenAccess in the running transaction.

 

Database db = Database.Get("DatabaseConnection1"); 
db.Properties.TransactionProvider = TransactionProvider.Explicit; 
IObjectScope scope = db.GetObjectScope(); 
Person p = new Person { Name = "Jan" }; 
using (CommittableTransaction txn = new CommittableTransaction()) 
    scope.EnlistTransaction(txn); 
    scope.Add(p); 
    txn.Commit(); 
using (CommittableTransaction txn = new CommittableTransaction()) 
    scope.EnlistTransaction(txn); 
    p.Name = "Changed"
    txn.Rollback(); 
 

 

If you use the System.Transactions integration you have a 2 phase commit immediately, the object scope and the underlying ADO.NET connection are both registered in the transaction. This guarantees that both resources are handled together and you that can reuse the same scope in another transaction.

If you do not want this overhead and if you do not need 2-phase commit you should use one of the two OpenAccess transaction scenarios.



About the Author

Thomas Krueger

 is Senior Software Architect in OpenAccess ORM Team

Comments

Comments are disabled in preview mode.