XPO Level Transactions
- 4 minutes to read
Long-running database level transactions can cause concurrency issues in multi-user applications. Long-running XPO level transactions are not affected by this problem, because XPO initiates a short database transaction only when it is about to commit changes.
To take advantage of XPO level transactions, use the UnitOfWork component. UnitOfWork tracks created, deleted, and modified objects and automatically begins a transaction.
#Save and Rollback Changes
#Save Changes
To save created, deleted, and modified objects, call the UnitOfWork.CommitChanges method. During this operation, UnitOfWork raises the following events: Session.FailedFlushChanges, Session.FailedCommitTransaction.
using(UnitOfWork uow = new UnitOfWork()) {
Person p1 = new Person(uow, "Mike");
// p1.Save(); when working with a Session
p2 = new Person(uow, "John");
// p2.Save(); when working with a Session
p3 = new Person(uow);
p.Name = "Bob";
p.Location = "US";
// p3.Save(); when working with a Session
// Save all the changes made
uow.CommitChanges();
}
The UnitOfWork.CommitChanges method may throw an exception if a failure occurs when XPO commits the transaction. You can handle such exceptions in any of the following ways.
- Show the error message and let a user to reload/change the data and save it again.
- Automatically discard all changes and reload changed objects.
#Discard Changes
Dispose of a UnitOfWork instance to discard all changes. To discard all changes without disposing of the current UnitOfWork instance, call the UnitOfWork.ReloadChangedObjects method.
Note
Do not use legacy Drop
#Isolation
A new XPCollection instance does not consider uncommitted changes that have been made in an XPO level transaction. XPCollection fetches data from the database where unsaved changes do not exist.
You can make new XPCollection instances consider changes that are in transaction: pass the PersistentCriteriaEvaluationBehavior.InTransaction value as the criteriaEvaluationBehavior parameter to the XPCollection constructor:
using (UnitOfWork unitOfWork = new UnitOfWork()) {
// ...
people = new XPCollection<Person>(PersistentCriteriaEvaluationBehavior.InTransaction,
unitOfWork, null);
}
Note
Performance
When you use the In
The code below shows how the InTransaction behavior works. The first collection does not contain a recently created object. The second collection contains both the old and the new object.
// Clear the database and create an object.
using (Session session = new Session()) {
session.ClearDatabase();
new Person(session, "Willy Watt").Save();
}
using (UnitOfWork unitOfWork = new UnitOfWork()) {
// Create the second object.
Person person = new Person(unitOfWork, "Billy Bott");
person.Save();
XPCollection<Person> people = new XPCollection<Person>(unitOfWork);
// This collection does not see the second object.
Debug.Assert(people.Count == 1, "Wrong count");
people = new XPCollection<Person>(PersistentCriteriaEvaluationBehavior.InTransaction,
unitOfWork, null);
// This collection sees both objects.
Debug.Assert(people.Count == 2, "Wrong count");
}