Blogs

Gary's Blog

XPO - Explicit Transactions (Coming in v2010 Vol 2)

     

Coming in 2010.2 is the new ExplicitUnitOfWork. This class derives from UnitOfWork and allows you to easily use explicit transactions in XPO. The advantage of this is that it works with objects as if they were already stored in the database, meaning it appears – to the developer – as if there is no difference between objects in memory and data in the database. The only downside of this is that the class must be the exclusive owner of the database connection.

Let’s demonstrate this with an example. First, we will declare a Person class:

using DevExpress.Xpo;

 public class Person: XPLiteObject {
     int oid;
     string name;
     int age;
     
     [Key(true)]
     public int Oid {
         get { return oid; }
         set { SetPropertyValue<int>("Oid", ref oid, value); }
     }

     public string Name {
         get { return name; }
         set { SetPropertyValue<string>("Name", ref name, value); }
     }

     public int Age {
         get { return age; }
         set { SetPropertyValue<int>("Age", ref age, value); }
     }

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

And then make a few tests with the ExplicitUnitOfWork:

using System;
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Data.Filtering;

CriteriaOperator criteria;

//Assume the database is empty

using(ExplicitUnitOfWork euow = new ExplicitUnitOfWork(dataLayer)) {
    Person person = new Person(euow);
    person.Name = "Thomas Brown";
    person.Age = 34;

    //Try to load from the database object with the same name and the age of
    criteria = CriteriaOperator.Parse("Name = ? AND Age = ?", person.Name, person.Age);
    Person personFromDB = euow.FindObject<Person>(criteria);

    //Check that the database loaded the newly created object
    System.Diagnostics.Debug.Assert(ReferenceEquals(person, personFromDB));
    System.Diagnostics.Debug.Assert(person.Oid == personFromDB.Oid);
}

using(ExplicitUnitOfWork euow = new ExplicitUnitOfWork(dataLayer)) {
    //Try again to load the object from the DB
    Person personFromDB = euow.FindObject<Person>(criteria);

    //At this time the object is not loaded, because the changes we do not save
    System.Diagnostics.Debug.Assert(ReferenceEquals(personFromDB, null));
}

Note, all of the above applies to objects loaded via XPCollection and XPView.

So, how does this work? Well, the trick is that ExplicitUnitOfWork opens an explicit transaction when modifying or creating any object. When loading, it saves all of the changes to the database so that it contains the modified objects. When the CommitChanges method is called, the ExplicitUnitOfWork saves the last modification and closes the explicit transaction. After that, all the changes are visible to the entire database.

Let’s modify our previous example:

using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Data.Filtering;

CriteriaOperator criteria;
int personOid;

using(ExplicitUnitOfWork euow = new ExplicitUnitOfWork(dataLayer)) {
    Person person = new Person(euow);
    person.Name = "Thomas Brown";
    person.Age = 34;

    criteria = CriteriaOperator.Parse("Name = ? AND Age = ?", person.Name, person.Age);
    Person personFromDB = euow.FindObject<Person>(criteria);

    System.Diagnostics.Debug.Assert(person.Oid == personFromDB.Oid);
    System.Diagnostics.Debug.Assert(ReferenceEquals(person, personFromDB));

    //Remember the OID of the object
    personOid = person.Oid;

    //This time will commit changes
    euow.CommitChanges();
}

using(ExplicitUnitOfWork euow = new ExplicitUnitOfWork(dataLayer)) {
    //Try again to load the object from the DB
    Person personFromDB = euow.FindObject<Person>(criteria);

    //Object loaded...
    System.Diagnostics.Debug.Assert(!ReferenceEquals(personFromDB, null));

    //...and the object of one buyout, we retain
    System.Diagnostics.Debug.Assert(personFromDB.Oid == personOid);
}

Now let’s think of a scenario, in which the ExplicitUnitOfWork may come in handy. Suppose there are fields, which have unique constraints in the database, and we need to make changes that can violate these constraints. In other words, we would need to remove the object and create a new one, but with the same identifier.

Let’s modify our Person class and make its Oid field not auto-incremented:

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

And then do the following:

using(ExplicitUnitOfWork euow = new ExplicitUnitOfWork(dataLayer)) {
    //Load the object with a key ‘5’
    Person person = euow.GetObjectByKey<Person>(5);

    //Delete it
    euow.Delete(person);

    //Save the changes within a transaction
    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 close the transaction
    euow.CommitChanges();
}

We have also created another example demonstrating the use of the ExplicitUnitOfWork class. You can download it and check it out, after 2010.2 ships. In this sample you will see:

“…a general solution, showing how to use the ExplicitUnitOfWork class to generate sequential numbers, that can then be used as user-friendly identifiers in documents, as invoice line numbers, etc. Here, as opposed to the solution shown in the A2213 KB Article, the ExplicitUnitOfWork class is used to ensure that the assignment of the sequential number will be a part of a database transaction that results in the successful saving of the document. Hence, the current solution will also work in a scenario with high-volume of concurrency transactions.”

That’s all for this blog post, ‘til next time, happy XPO’ing! Smile

Published Oct 26 2010, 02:20 PM by Gary Short (DevExpress)
Filed under: ,
Technorati tags: v2010.2, XPO
Bookmark and Share

Comments

 

Nate Laff said:

Will we then be able to get an Oid from an unsaved object? I have a lot of places this can come in handly. In my objects I'll do a pattern such as..

if (Session.IsNewObject(this)) { do something to get objects i'm after } else { use criteria to get those objects }

it seems like this pattern could be replaced.

October 26, 2010 9:58 AM
 

wolfgang hauer said:

Very nice - Waiting since XPO 2.0 for this!!

Wolfgang

October 26, 2010 10:47 AM
 

Steven Rasmussen said:

Keep bringing on the new features for XPO and XAF!  Although I'm still waiting for the BIG announcements about Domain Components and security enhancements. :)

October 26, 2010 3:20 PM
 

Michael Proctor [DX-Squad] said:

Very happy to see this guys, great work.

October 26, 2010 5:52 PM
 

Ralph Rutschmann said:

> ...showing how to use the ExplicitUnitOfWork class to generate sequential numbers...

Thats great! :-)

I know some guys will need this urgently!

November 4, 2010 2:15 AM
 

Francois Nel said:

This could be dangerous when used in UI forms, especially when you open something like a modal edit screen or allow a user to do multiple object changes and let the user decide when to commit/rollback the changes (could lead to 'deadlocks'). However,  this is a great feature for processing data and allowing the DB engine ensure data integrity!

If used right, this could be very powerful!

November 4, 2010 2:19 AM
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.