The requested page is not available for the requested platform. You are viewing the content for .NET Framework 4.5.2+ platform.

Explicit Units of Work

An explicit unit of work is a unit of work descendant that uses long explicit transactions to isolate object changes at the database level. Because of this, persistent objects are temporarily stored in a database, allowing you to access these objects as if they were already stored in the database.

Important

An Explicit Unit of Work that starts an explicit transaction must be the exclusive owner of the database connection. Therefore, only one explicit transaction can be open at a time. That's why you cannot use an Explicit Unit of Work within another Explicit Unit of Work. However, you can create Nested Units of Work within an Explicit Unit of Work, if necessary.

Managing Explicit Transactions

Within an explicit unit of work, you can execute transaction commands (start an explicit transaction, commit all temporary changes or roll them back) at any time, just like regular transactions, using methods displayed in the following table.

Transaction Command

Method

BEGIN

ExplicitUnitOfWork.BeginTransaction

COMMIT

ExplicitUnitOfWork.CommitTransaction

UnitOfWork.CommitChanges

UnitOfWork.CommitChangesAsync

ExplicitUnitOfWork.CommitTransactionAsync

ROLLBACK

ExplicitUnitOfWork.RollbackTransaction

As with regular transactions, the BEGIN command marks the point at which the data referenced by an explicit unit of work is consistent. All data modifications made after this command can be rolled back. This allows data to be returned to this known state of consistency. Each transaction lasts until either the COMMIT or ROLLBACK command is executed. The COMMIT command writes the modifications. The ROLLBACK command discards all the changes made.

To make things simple, an explicit unit of work automatically starts an explicit transaction before data modifications are temporarily saved to a database for the first time. So, there is no need to start an explicit transaction manually within an explicit unit of work.


using (ExplicitUnitOfWork euow = new ExplicitUnitOfWork(session1.DataLayer)) {
    // Starts tracking changes to persistent objects.
    Person person = new Person(euow);
    person.Name = "Thomas Brown";
    person.Age = 33;

    // Starts an explicit transaction and flushes all intermediate object changes to a database.
    // Thus, person has been temporarily stored to a database and FindObject locates this person.
    Person savedPerson = euow.FindObject<Person>(CriteriaOperator.Parse(
        "Name = ? And Age = ?", person.Name, person.Age));

    savedPerson.Name = "James Smith";
    savedPerson.Age = 60;

    // Updates person with new data and commits the explicit transaction.
    euow.CommitChanges();
}

Managing Object Changes

Like normal units of work, explicit units of work automatically track all changes made to persistent objects. Due to the explicit transactions, you can access modified objects without having to commit the changes beforehand. As shown in the example above, the newly created Person object is retrieved by the Session.FindObject<ClassType> function, as if it was already stored in a database.

In addition, you can manually manage changes within an explicit unit of work via the Session.DropChanges, Session.FlushChanges, and Session.FlushChangesAsync methods, as shown in the example below.


using (ExplicitUnitOfWork euow = new ExplicitUnitOfWork(session1.DataLayer))
{
    Person person1 = new Person(euow); // Starts tracking changes to persistent objects
    person1.Name = "Thomas Brown";
    person1.Age = 33;
    // Starts an explicit transaction and saves person1 to a database.
    euow.FlushChanges();

    // Create person2 with the same name.
    Person person2 = new Person(euow);
    person2.Name = person1.Name;
    person2.Age = 60;
    // Discards person2.
    euow.DropChanges();

    // The collection retrieves only person1.
    XPCollection<Person> savedPerson = new XPCollection<Person>(euow, 
        CriteriaOperator.Parse("Name = ?", person1.Name));
    // ...
}
Note

Methods performing transaction operations (see the table in the previous section) automatically call Session.DropChanges (ROLLBACK) or Session.FlushChanges (BEGIN OR COMMIT) for your convenience.

Managing object changes using explicit units of work is handy when intermediate object updates may violate database constraints. So to avoid constraint violations, all intermediate object changes are applied temporarily within an explicit unit of work, using Session.FlushChanges.

Consider an example when a field has a unique constraint in the database, and you need to make changes that can violate these constraints. In other words, you need to remove the object and create a new one, but with the same identifier. The code below shows how this can be accomplished. Note that the Oid key field is not auto-incremented.


public class Person: XPLiteObject {
     int oid;

     [Key]
     public int Oid {
         get { return oid; }
         set { SetPropertyValue<int>("Oid", ref oid, value); }
     }

     // ...
     public Person(Session session): base(session) {
     }
 }

// ...

using(ExplicitUnitOfWork euow = new ExplicitUnitOfWork(session1.DataLayer)) {
    // Load person with a key '5'.
    Person person = euow.GetObjectByKey<Person>(5);

    // Delete it.
    euow.Delete(person);

    // Start an explicit transaction and flush all intermediate object changes to a database.
    euow.FlushChanges();

    // Create a new object with a key '5'.
    Person personNew = new Person(euow);
    personNew.Oid = 5;
    personNew.Name = "Thomas Brown";
    personNew.Age = 34;

    // Save everything and commit the explicit transaction.
    euow.CommitChanges();
}

Note that when using explicit units of work, your persistent classes should not make database queries from IXPObject.OnSaving method overloads. Thus, you cannot modify an object state via the OnSaving method.

See Also