Introduction
Wouldn't it be nice if we could (like in a database) take an object-oriented domain model, start a transaction, do wild things with the data and relationships, and then eventually just roll back these changes? My opinion: Yes, it would! Especially if we tend to use as "less" as possible of the functionalities of a relational database and as much as possible the benefits of object orientation. In those system designs, the possibility of managing object model transactions is necessary. For the TechNewLogic "Stasy" framework - a high level implementation of the Unit-Of-Work design pattern (http://martinfowler.com/eaaCatalog/unitOfWork.html), I wrote a little demo extension that makes transactions possible.
Summary
- Automatic tracking of property changes via a specialized lightweight .NET proxy.
- The proxy inherits the entity types at runtime.
- These types must be registered at the proxy generator (see example below).
- Limitation: The entity classes of the object model have to be created via a factory method of the proxy generator.
- Limitation: The entity classes must have a default (parameter-less) constructor.
- Limitation: All properties of the entity classes must be declared as "virtual" so that the runtime inheritance mechanism of the proxy can work.
- Limitation: If lists or collections are used in the entity classes, an implementation of
ITransactionList
has to be used (there is a standard implementation in the framework).
Example
public class EntityClass1
{
public EntityClass1()
{
EntityClass2List = new TransactionList<EntityClass2>();
}
public virtual int MyInt { get; set; }
public virtual TransactionList<EntityClass2> EntityClass2List { get; set; }
}
public class EntityClass2
{
public virtual string MyString { get; set; }
}
Here, we have quite a simple object model. It consists of two classes: one has an aggregation to the other and some primitive properties.
The following code snippet demonstrates how to use the transaction framework. Basically, it works like this:
- Create some data (we use our small object model, of course).
- Begin a transaction.
- Now, we do some stuff with our model, change values, insert some new entities, etc.
- Rollback
- Result: The object model looks as if nothing happened!
static void Main(string[] args)
{
var transactionMonitor = new TransactionMonitor();
var generator = new ProxyGenerator(
new[]
{
typeof(EntityClass1),
typeof(EntityClass2)
},
transactionMonitor);
var entity1 = generator.CreateProxy<EntityClass1>();
entity1.MyInt = 100;
AssertAreEqual(100, entity1.MyInt);
entity1.EntityClass2List.Add(generator.CreateProxy<EntityClass2>());
var entity2 = entity1.EntityClass2List.Single();
entity2.MyString = "Hallo";
AssertAreEqual("Hallo", entity2.MyString);
transactionMonitor.Begin();
entity1.MyInt = 9999;
AssertAreEqual(9999, entity1.MyInt);
entity2.MyString = "Welt";
AssertAreEqual("Welt", entity2.MyString);
var newEntity = generator.CreateProxy<EntityClass2>();
entity1.EntityClass2List.Add(newEntity);
AssertAreEqual(2, entity1.EntityClass2List.Count);
entity1.EntityClass2List.Remove(entity2);
var checkEntity = entity1.EntityClass2List.Single();
AssertAreEqual(newEntity, checkEntity);
transactionMonitor.Rollback();
AssertAreEqual(100, entity1.MyInt);
var checkEntity2 = entity1.EntityClass2List.Single();
AssertAreEqual(entity2, checkEntity2);
AssertAreEqual("Hallo", entity2.MyString);
Console.ReadLine();
}
static void AssertAreEqual(object o1, object o2)
{
if (o1 == null && o2 == null)
return;
else if (o1 == null && o2 != null)
throw new ApplicationException();
else if (o1 != null && o2 == null)
throw new ApplicationException();
else if (!o1.Equals(o2))
throw new ApplicationException();
}
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.