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