Skip to main content
.NET 6.0+

ExplicitUnitOfWork Class

The ‘unit of work’ that uses long database level transaction to allow you to simplify the management of object changes and to access modified objects without having to commit the changes beforehand.

Namespace: DevExpress.Xpo

Assembly: DevExpress.Xpo.v23.2.dll

NuGet Package: DevExpress.Xpo

Declaration

public class ExplicitUnitOfWork :
    UnitOfWork

Remarks

An explicit unit of work is a UnitOfWork descendant that uses long database level transaction 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 database level transaction must be the exclusive owner of the database connection. Therefore, only one database level 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 Database Level Transaction

Within an explicit unit of work, you can execute transaction commands (start a database level 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 a database level transaction before data modifications are temporarily saved to a database for the first time. So, there is no need to start a database level 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 a database level 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 database level transaction.
    euow.CommitChanges();
}

Managing Object Changes

Like regular units of work, explicit units of work automatically track all changes made to persistent objects. Due to the database level 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 a database level 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. 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>(nameof(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 a database level 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 database level 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.

Implements

See Also