Click here to Skip to main content
15,881,812 members
Articles / Programming Languages / C#

ObjectLounge - An Object-Oriented Database Framework

Rate me:
Please Sign up or sign in to vote.
4.07/5 (8 votes)
30 Jul 2009LGPL310 min read 43.6K   425   45   21
Host your Domain Models "Out Of The Box" + No storage or schema needed + Transactions on Domain Models + Validation
ObjectLounge Logo

Introduction

The TechNewLogic ObjectLounge framework is an open source framework that helps you developing business applications. It is a host for object oriented domain models that manages concurrency, transactions, validation and persistence.

You can get an impression on how to work with the framework and what it does if you have a look at the small-step tutorial below.

You want to know more? Go to http://objectlounge.technewlogic.de.

Prerequisites

You will need the Microsoft SQL Server CE 3.5 SP1 Runtime

Background

Today, people automatically use a relational database if they want to build a business application. But:

<quot>Databases come from a long time ago in a galaxy far, far away...

In this time,

  • RAM was very small and very expensive,
  • Computers were pretty slow,
  • Programming was something very technnical and abstractions like OO had not been invented yet.

Thus, the database vendors optimized their products on scaling, memory sufficiency, etc. But is that really needed in a middle sized "as usual" business application? We think: No, Sir. Reasons:

  • 8 GB of RAM cost only some bucks.
  • Even desktop computers run at several GHz and have multiple cores.
  • Software is everywhere and has to change very fast and be very flexible.
  • The amount of data in a middle sized business application is usually less then the amount of memory on a well equipped machine.

Object Orientation is awesome and cool and smooth. But as soon as you hit the database, it becomes not so cool and smooth anymore. We define relational tables, but we want to have hierarchical classes. We write stored procedures, functions and triggers, but we want to encapsulate our logic in objects with methods, properties and events. The point is: Even with modern ORMs we have to go through a lot of hassle to connect our application to a database. What we get for that is a lot of features we don't need. So the question is:

<quot>Why should we use a data base at all?

The only thing we need is a store where we can persist our entities and a framework that makes this aspect as transparent as possible. But what about the "useful" features of a database which are defined by the ACID criteria, which are basically focused on concurrency? In a database, we can run transactions, we can roll them back and we have isolation between transactions. If we want to get rid of our database, we need a framework that handles transactions with rollback and isolation on our domain model - fine! With ObjectLounge, you can do all these things, too - without a database!

Tutorial

The Example is focused on developing a standalone (client-only) WPF application that manages a blog system with Authors, Blogs and Posts (inspired by the "Getting Started" of Castle's ActiveRecord). Of course it can also be used in services, where the transaction / concurrency handling of ObjectLounge come into play.

The goal of the Sample is to give you an idea of

  • How should domain classes look like.
  • How we can use abstractions like Aggregation and Composition.
  • How to work with the domain model (querying data, manipulating data, transactions and persistence).

What this Sample does not show:

  • Validation
  • Complex Business Logic
  • Concurency

Setup your project

For this sample you start developing with ObjectLounge by doing the following:

  • Create a new "WPF Application" project in Visual Studio 2008 and name it "GettingStartedDemo".
  • Add a reference to the assembly Technewlogic.ObjectLounge.dll.

Creating the Domain Model

Now, create a folder in the project called "DomainModel" and add the following new classes:

  • Author
  • Blog
  • PostCategory
  • Post
  • DomainModelContext

The first 4 classes are called "entity classes" and contain the business logic of our application. Together, they form the domain model of the sample application. The DomainModelContext class holds lists of the 4 entity classes (either read-only or read/write).

The final domain model will look like this:

Domain Model

Now, we will look at the classes in detail and describe how they interact with the ObjectLounge framework. You may copy the code regions into your classes.

Author class

C#
using System;
using System.ComponentModel;
using Technewlogic.ObjectLounge;

namespace GettingStartedDemo.DomainModel
{
    [Identity("_id")]
    public class Author : INotifyPropertyChanged
    {
        protected Guid _id = Guid.NewGuid();

        private string _name = default(string);
        public virtual string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }

        private string _password = default(string);
        public virtual string Password
        {
            get { return _password; }
            set
            {
                _password = value;
                OnPropertyChanged("Password");
            }
        }

        private Blog _blog;
        [Composition]
        public virtual Blog Blog
        {
            get { return _blog; }
            set
            {
                _blog = value;
                OnPropertyChanged("Blog");
            }
        }

        #region INotifyPropertyChanged Members

        ...

        #endregion
    }
}
Simple POCO
As you can see, the Author class is a simple POCO (plain old CLR object). This means you don't have to implement ObjectLounge-specific interfaces or inherit a base class if you want to use ObjectLounge. The implementation of INotifyPropertyChanged is just for databinding reasons in WPF.
IdentityAttribute - The Entity's Identity
Each ObjectLounge entity class needs the IdentityAttribute that specifies the name of the field that should be used to maintain the object's identity. Identity is also important for the ObjectLounge reference management system which relies on identity fields inside the entity (this is one point where the persistence aspect of the framework is not transparent to the framework user).
Virtual Properties
Have a look at the Name or the Password properties: They are marked as virtual! This is mandatory because internally, the ObjectLounge framework generates proxies of your domain classes by inheriting them at runtime (this is important for almost all the subsystems like transaction management, change tracking and validation).
CompositionAttribute - Manage a Child Reference
There's another property: The Blog property, which is a pointer to a blog entity. It is marked with the CompositionAttribute. In ObjectLounge, references (either single or list references) are modeled by using compositions and aggregations. Compositions are the "owner" of their children. That means, if the parent entity is deleted, all children are also deleted from the store. There can only be ONE class having a composition to another class. In this case: If the Author class defines a composition to the Blog class, no other class could have a composition to the Blog class, too.

Blog class

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Technewlogic.ObjectLounge;

namespace GettingStartedDemo.DomainModel
{
    [Identity("_id")]
    public class Blog : INotifyPropertyChanged
    {
        protected Guid _id = Guid.NewGuid();

        private string _name = default(string);
        public virtual string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }

        [MasterRef]
        public virtual Author Author { get; protected set; }

        [Composition]
        public virtual IList<Post> Posts { get; protected set; }

        #region INotifyPropertyChanged Members

        ...

        #endregion
    }
}
MasterRefAttribute - Backpointer to the Parent
In object oriented domain models, you can navigate through your object graph with a lot of comfort. In our case, we could pick ourself an author and navigate to it's blog using the Blog property. What if we want to navigate backwards - from the Blog to it's parent Author? For this reason, you can specify a backpointer property which is decorated by the MasterRefAttribute. The framework will automatically set the property when you change the parent's composition.
CompositionAttribute - Manage Child Collections
Additionally, we have - analog to the Author class - a composition to the Post class. But here, we don't have a simple link, but a whole list of posts. When we use this class later, the framework will assign the appropriate collection to the IList property.

PostCategory class

C#
using System;
using System.ComponentModel;
using Technewlogic.ObjectLounge;

namespace GettingStartedDemo.DomainModel
{
    [Identity("_id")]
    public class PostCategory : INotifyPropertyChanged
    {
        protected Guid _id = Guid.NewGuid();

        private string _name = default(string);
        public virtual string Name
        ...
    }
}

Pretty simple class :)

Post class

C#
using System;
using System.ComponentModel;
using Technewlogic.ObjectLounge;

namespace GettingStartedDemo.DomainModel
{
    [Identity("_id")]
    public class Post : INotifyPropertyChanged
    {
        public Post()
        {
            CreationDate = DateTime.Now;
        }

        protected Guid _id = Guid.NewGuid();

        private string _heading = default(string);
        public virtual string Heading
        ...

        private string _content = default(string);
        public virtual string Content
        ...

        private DateTime _creationDate = default(DateTime);
        public virtual DateTime CreationDate
        ...

        private bool _isPublished = default(bool);
        public virtual bool IsPublished
        ...

        [MasterRef]
        public virtual Blog Blog { get; protected set; }

        [Aggregation]
        public virtual PostCategory PostCategory { get; set; }

        ...
    }
}
AggregationAttribute - Weak References to Entities
As we have seen before, Posts are composed ("owned") by the Blog class. But there is another interesting fact here, which is: The Post needs a PostCategory. But not in the way that it is really the "owner" of it. We just want to point to an existing PostCategory - not more. So the character of the PostCategory is more like that of a master data. This is achieved by using an AggregationAttribute over the PostCategory property.

DomainModelContext class

C#
using System.Collections.Generic;
using Technewlogic.ObjectLounge;

namespace GettingStartedDemo.DomainModel
{
    public class DomainModelContext
    {
        [EntityCollection]
        public IList<Author> Authors
        {
            get; private set;
        }

        [EntityCollection]
        public IEnumerable<Blog> Blogs
        {
            get; private set;
        }

        [EntityCollection]
        public IEnumerable<Post> Posts
        {
            get; private set;
        }

        [EntityCollection]
        public IList<PostCategory> PostCategorys
        {
            get; private set;
        }
    }
}

Last, but not least: We need a class that gives us access to all the entities in the system, which we call "Context".

EntityCollectionAttribute
The EntityCollectionAttribute tells the ObjectLounge framework which entity classes our domain model contains.
Top-Level Classes
Top-Level classes have no "owner", means: No other class holds has composition to it. Thus we need something where we can insert new entities or delete old ones, by using the IList<T> properties in the context.
Non Top-Level Classes
Non Top-Level classes can only be read through IEnumerable<T> properties in the context. Write access is provided via the compositions in the entity's parent classes.

Working with the Domain Model

In the following section, we will have a look on how to work with our domain model, i.e. modifying and querying data. Most of the interesting are done in the sample application's MainWindow class.

Startup

C#
using System;
using System.ComponentModel;
using Technewlogic.ObjectLounge;

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeObjectLounge();
        
        ...
    }

    private Engine<DomainModelContext> _engine;
    public DomainModelContext Context { get; private set; }

    private void InitializeObjectLounge()
    {
        // Create a new Context.
        Context = new DomainModelContext();

        // Start the framework (the database will be created
        // automatically if it doesn't exist).
        _engine = EngineFactory.CreateEngine("store.sdf", Context);

        // Check and create the demo user if not present.
        if (!Context.Authors.Any(it => it.Name == "Demo"))
        {
            // Execute an atomic transaction and store the "demo" user.
            _engine.ExecuteTransaction(() =>
            {
                // We must use the factory method of the engine
                // to create new entities.
                var demoAuthor = _engine.CreateInstance<Author>();
                demoAuthor.Name = "Demo";
                demoAuthor.Password = "Demo123";
                Context.Authors.Add(demoAuthor);
            });
        }
    }

    ...
    
}
Context Creation
We have to supply the framework with an instance of our DomainModelContext. When we start the engine, all the entity collections will be injected automatically.
Gentlemen, Start Your Engines!
The framework is started using the EngineFactory. You can either supply a path to the database file. In that case, ObjectLounge uses it's out-of-the-box SQL-Server CE backend. Alternatively, you can provide your own backend implementation. In that case, the data access is completely in your hands.
When the framework starts, a lot of things happen. The framework checks that your domain model (the entity classes and the context) meet the constraints described above. After this check was successful, the framework loads the entities from the store and builds up the provided context. Now, we can start working!
Execute an Atomic Transaction
If the database is created the first time, no data is present at all. Thus, we create a "demo" user we can use for the Login form to login to the application.
You can use LINQ to query data from the context at any time. If you want to modify properties or add / remove entities, you have to do this inside of a transaction. Transactions in ObjectLounge are thread-bound, which means there can be only one running transaction per thread. Thus our sample application is a single-threaded client application, this is not so important here.
There are two ways you can execute a transaction:
  • Use the ExecuteTransaction method of the engine. The provided delegate is executed and committed, or rolled back if an exception occured.
  • Use the BeginTransaction method of the engine to control the transaction flow (which is not used in this sample).
Transaction Behavior
All changes that are made while a transaction is running are immediately visible in your entities if you access them from the transaction which modified the entities (as you would expect from normal objects). If you decide in any point of time that you don't want the changes, you can just rollback the changes and it is as if nothing ever happened (you don't have to reload or rebuild your entities in any way). Only if a transaction is committed, the changes are actually persisted to the data store.
Creating new Entity Instances
To enable the ObjectLounge framework managing entities for you, you have to let it create new entities for you. So, instead of using the new operator, you have to use the CreateInstance<T> factory method of the engine. This requires a parameterless (default) constructor in the entity classes.

DataBinding

We have simple POCOs, so we can bind the entities in our context to UI elements. Note that all collections in ObjectLounge implement INotifyCollectionChanged, which is good for databinding to ItemsControl, ListBox, etc.

XML
<Window x:Class="GettingStartedDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:domainModel="clr-namespace:GettingStartedDemo.DomainModel"
    x:Name="mainWindow">

    <ListBox ItemsSource="{Binding ElementName=mainWindow, Path=Context.PostCategorys}">
            
        ...

Summary

We have learned some important things when we work with ObjectLounge:

  • There is a domain model that consists of entity classes and a context class.
  • The context class has list properties of the entity classes which are marked with the EntityCollectionAttribute.
  • Entity classes are POCOs.
  • The relationships between entities are modeled using the CompositionAttribute, the AggregationAttribute, and the MasterRefAttribute.
  • The management of the ObjectLounge framework during runtime is done using the Engine.
  • An engine can be created using the EngineFactory method.
  • Instances are created using the CreateInstance method of the engine.
  • Reading and querying data is always possible.
  • Data can only be modified within a transaction.

History

2009-07-20: Article Posted.
2009-07-30: The list property of the Blog class marked as virtual / MasterRef setters marked as protected.

License

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


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

 
GeneralArkeos Factory [modified] Pin
Member 75483730-Jul-09 4:22
Member 75483730-Jul-09 4:22 
GeneralMy vote of 1 Pin
Graham Downs29-Jul-09 1:09
Graham Downs29-Jul-09 1:09 
GeneralRe: My vote of 1 [modified] Pin
bilo8129-Jul-09 12:46
bilo8129-Jul-09 12:46 
GeneralRe: My vote of 1 Pin
Ronald Schlenker29-Jul-09 23:09
Ronald Schlenker29-Jul-09 23:09 
GeneralRe: My vote of 1 Pin
bilo8130-Jul-09 2:18
bilo8130-Jul-09 2:18 
GeneralRe: My vote of 1 Pin
Ronald Schlenker29-Jul-09 22:52
Ronald Schlenker29-Jul-09 22:52 
QuestionCan you do two-way data binding? Pin
myker28-Jul-09 17:58
myker28-Jul-09 17:58 
Great article! Love it! I re-created your demo using the sample code, and as an experiment, tried to bind the IList<author> Authors property in the DomainModelContext to a Data Grid in WPF (using the data grid in the WPF toolkit). This worked well once I defined the columns.

The trouble arises when I double-click an item in the datagrid to edit it. I get the "'EditItem' is not allowed for this view" exception, which essentially means that WPF views the collection as Read Only. I also attempted to use BeginTransaction in the BeginningEdit event of the grid, and Commit transaction on the CellEndEditing event so I could wrap the edit into a transaction. I still received the exception.

Is there support for two-way binding?
AnswerRe: Can you do two-way data binding? Pin
Ronald Schlenker28-Jul-09 19:54
Ronald Schlenker28-Jul-09 19:54 
GeneralRe: Can you do two-way data binding? Pin
Ronald Schlenker28-Jul-09 22:26
Ronald Schlenker28-Jul-09 22:26 
GeneralDirty Model Pin
Gonzalo Brusella28-Jul-09 6:57
Gonzalo Brusella28-Jul-09 6:57 
GeneralRe: Dirty Model Pin
kraemersoftware.com2-Aug-09 22:48
kraemersoftware.com2-Aug-09 22:48 
QuestionMultiUser? Pin
georani20-Jul-09 7:19
georani20-Jul-09 7:19 
AnswerRe: MultiUser? [modified] Pin
Ronald Schlenker20-Jul-09 7:48
Ronald Schlenker20-Jul-09 7:48 
QuestionRe: MultiUser? Pin
georani20-Jul-09 8:36
georani20-Jul-09 8:36 
AnswerRe: MultiUser? Pin
Ronald Schlenker20-Jul-09 22:51
Ronald Schlenker20-Jul-09 22:51 
QuestionRe: MultiUser? Pin
georani21-Jul-09 2:02
georani21-Jul-09 2:02 
AnswerRe: MultiUser? Pin
Ronald Schlenker21-Jul-09 2:07
Ronald Schlenker21-Jul-09 2:07 
QuestionRe: MultiUser? Pin
georani21-Jul-09 4:31
georani21-Jul-09 4:31 
AnswerRe: MultiUser? Pin
Ronald Schlenker21-Jul-09 6:01
Ronald Schlenker21-Jul-09 6:01 
QuestionSupported Databases? Pin
CreF20-Jul-09 4:19
professionalCreF20-Jul-09 4:19 
AnswerRe: Supported Databases? Pin
Ronald Schlenker20-Jul-09 4:33
Ronald Schlenker20-Jul-09 4:33 

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.