Click here to Skip to main content
15,891,033 members
Articles / Programming Languages / C#

Transactions on object models

Rate me:
Please Sign up or sign in to vote.
4.00/5 (5 votes)
8 Mar 2009GPL32 min read 33.2K   228   20   4
Rollback or commit changes that you did on .NET objects.

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

C#
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!
C#
static void Main(string[] args)
{
    // Setup
    var transactionMonitor = new TransactionMonitor();
    var generator = new ProxyGenerator(
        new[]
        {
            typeof(EntityClass1),
            typeof(EntityClass2)
        },
        transactionMonitor);

    // Create the model data
    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);

    
    ///
    /// everything done from here will be tracked and eventually rolled back
    ///
    transactionMonitor.Begin(); 

    // change a value property of our entity1
    entity1.MyInt = 9999;
    AssertAreEqual(9999, entity1.MyInt); 

    // change a value property of our entity2
    entity2.MyString = "Welt";
    AssertAreEqual("Welt", entity2.MyString); 
    
    // add a new entity to the list of entity 1 and check if we had success
    var newEntity = generator.CreateProxy<EntityClass2>();
    entity1.EntityClass2List.Add(newEntity);
    AssertAreEqual(2, entity1.EntityClass2List.Count); 
    
    // remove the 'original' entity2; the only remaining
    // entity should be the new entity
    entity1.EntityClass2List.Remove(entity2);
    var checkEntity = entity1.EntityClass2List.Single();
    AssertAreEqual(newEntity, checkEntity); 
    
    ///
    /// Rollback all our changed
    /// 
    transactionMonitor.Rollback(); 
    
    // the value property of entity1
    AssertAreEqual(100, entity1.MyInt); 

    // there should be one entity in the entity list of entity1
    var checkEntity2 = entity1.EntityClass2List.Single();
    AssertAreEqual(entity2, checkEntity2); 

    // the value property of entity2
    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();
}

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior) www.technewlogic.de
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionDB Issue Pin
Bruce Zhang8-Mar-09 17:26
Bruce Zhang8-Mar-09 17:26 
AnswerRe: DB Issue Pin
Ronald Schlenker8-Mar-09 21:33
Ronald Schlenker8-Mar-09 21:33 
Hi Bruce,

Let me say it like this: What I described here is just one layer of a larger framework. This layer - the transaction layer - is not aware of the fact that there might be a database somewhere in your system; it just cares about your .Net object model. If you neeed synchronization with a database, you have to implement that on your own.

Additionally: As I described in the article, the implementation shown here is limited to one running transaction per time; I'm currently extending it for allowing concurrent access using the "Read-Committed" DB concurrency pattern.

May I ask which technology do you use for DB synchronization? Do you use some kind of Active Records?

Regards

Ronald
GeneralRe: DB Issue Pin
Bruce Zhang8-Mar-09 21:52
Bruce Zhang8-Mar-09 21:52 
GeneralRe: DB Issue Pin
Ronald Schlenker9-Mar-09 3:45
Ronald Schlenker9-Mar-09 3:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.