Click here to Skip to main content
Click here to Skip to main content
Go to top

Transactions on object models

, 8 Mar 2009
Rate this:
Please Sign up or sign in to vote.
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

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)
{
    // 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)

Share

About the Author

Ronald Schlenker
Software Developer (Senior) www.technewlogic.de
Germany Germany
No Biography provided

Comments and Discussions

 
QuestionDB Issue PinmemberBruce Zhang8-Mar-09 17:26 
AnswerRe: DB Issue PinmemberRonald 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 PinmemberBruce Zhang8-Mar-09 21:52 
GeneralRe: DB Issue PinmemberRonald Schlenker9-Mar-09 3:45 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 8 Mar 2009
Article Copyright 2009 by Ronald Schlenker
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid