Skip to main content
A newer version of this page is available. .

Using Transactions

  • 4 minutes to read

A transaction is a sequential group of manipulation operations, performed as if it were one single work unit. In other words, a transaction will never be complete unless each individual operation within the group is successful. If any operation within the transaction fails, the entire transaction will fail.

Transactions have the following four standard properties (ACID properties).

  • Atomicity: ensures that all operations within the work unit are completed successfully; otherwise, previous operations are rolled back to their former state.
  • Consistency: requires that data bound by a transaction be semantically preserved.
  • Isolation: the result of an individual transaction must be transparent to any other open transaction until that transaction commits successfully.
  • Durability: ensures that the result or effect of a committed transaction persists in case of a system failure.

Within XPO, you can use transactions both at the XPO level (regular transactions) and at a database level (explicit transactions). This document describes ways of working with regular transactions. To learn about explicit transactions, refer to Using Explicit Transactions.

Managing Transactions

To start a regular transaction, call a session’s Session.BeginTransaction method. If a Unit of Work is used, a transaction is started automatically.

Note

Even if you do not start transactions explicitly, XPO automatically starts transactions and commits them to perform persistent object manipulations such as saving or deleting an object. This process does not require your intervention, but still causes all transaction-related events such as Session.FailedCommitTransaction to be raised.

When a successful transaction is completed, a COMMIT command should be issued, so that changes to all involved persistent objects take effect. To do this, call the Session.CommitTransaction method. If a failure occurs, a ROLLBACK command should be issued to return every persistent object in the transaction to its previous state. In this instance, the Session.RollbackTransaction method must be used. It completes the transaction discarding all the changes made since the transaction was started.

Typically these methods are used in code like this:


...
using (Session session = new Session()) {
    session.BeginTransaction();
    try {
        // Create, update or delete objects
        session.CommitTransaction();
    }
    catch {
        session.RollbackTransaction();
        throw;
    }
}
...

Since transactions at the XPO level (regular transactions) do not correspond to transactions at a database level (explicit transactions), it is possible to have long-running regular transactions in XPO without promoting concurrency issues in multi-user use cases. It is only when a transaction is committed, that a short database transaction is initiated to apply the changes.

To see if a transaction is in progress, use the Session.InTransaction property.

Isolation

When a transaction is running and there are uncommitted changes in it - remember that transactions are also the basis of Units of Work and do not correspond to database transactions - a newly constructed XPCollection will not normally be able to “see” the changes that have been made in the current transaction. This is because the collection fetches its content from the database, but the changes have not yet been written there. This is default behavior; we have introduced a flag called PersistentCriteriaEvaluationBehavior that allows you to see a merged state of the data, where changes from the current running transaction are already included.

See the code below. The default collection - which uses the enum value PersistentCriteriaEvaluationBehavior.BeforeTransaction internally - does not see the newly created object, while the collection with the specified behavior PersistentCriteriaEvaluationBehavior.InTransaction finds both the old and new object.


// Clear the database and create one object.
using (Session session = new Session()) {
    session.ClearDatabase();
    new Person(session, "Willy Watt").Save();
}

using (UnitOfWork unitOfWork = new UnitOfWork()) {
    // Create a second object.
    Person person = new Person(unitOfWork, "Billy Bott");
    person.Save();

    XPCollection<Person> people = new XPCollection<Person>(unitOfWork);
    // This collection does not see the new 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");
}