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 DropChanges and RollbackTransaction methods to discard changes. These methods do not revert modified objects to their initial state. These methods are used for backward compatibility with existing applications.
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 InTransaction behavior, you may get performance degradation when XPO loads filtered data. When modified objects can affect the result, XPO can load more than expected or all records from the database.
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");
}