xpo-2111-crud-transactions-xpo-transactions.md
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.
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();
}
Dim uow As UnitOfWork = New UnitOfWork()
Dim p1 As Person = 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()
uow.Dispose()
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.
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.
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);
}
Using unitOfWork1 as UnitOfWork = New UnitOfWork
' ...
people = New XPCollection(Of Person)(PersistentCriteriaEvaluationBehavior.InTransaction, _
unitOfWork1, Nothing)
End Using
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");
}
' Clear the database and create an object.
Using session1 as Session = New Session
session1.ClearDatabase
New Person(session1, "Willy Watt").Save
End Using
Using unitOfWork1 as UnitOfWork = New UnitOfWork
' Create a second object.
Dim person As New Person(unitOfWork, "Billy Bott")
person.Save()
Dim people as XPCollection(Of Person) = New XPCollection(Of Person)(unitOfWork1)
' This collection does not see the second object.
Debug.Assert((people.Count = 1), "Wrong count")
people = New XPCollection(Of Person)(PersistentCriteriaEvaluationBehavior.InTransaction, _
unitOfWork1, Nothing)
' This collection sees both objects.
Debug.Assert((people.Count = 2), "Wrong count")
End Using