Sony Arouje

a programmer's log

Posts Tagged ‘db4o

Transparent Persistence with db4o

with 2 comments

I was working on a small Invoicing application for my brother and I uses db4o for data persistence. Again I used db4o here because of it’s simplicity and ease of doing future updates.

I started with POCO entities and db4o handles the insertions and deletion quiet well. I started experiencing trouble when I try to update object graphs. For e.g. I created a SaleOrder with two Items then I retrieve the order and added one more item and save the changes. db4o will never save the newly added Item, it always shows the items that added initially. Because db4o couldn’t make out the changes in the object graph. There are two ways to let db4o handle this scenario.

UpdateDepth

There is a problem with UpdateDepth, we have to define a depth say 5, 10, etc and db4o will traverse the graph to identify the changes, that means we should forecast our object graph depth. This also introduce performance issues. So I decided not to use it.

Transparent Persistence

With Transparent Persistence (TP) the object will tell which all the fields got modified and db4o will update the changed fields efficiently.

To enable Transparent Persistence we have to do some changes in

  • repository level
  • entity level.
  •  

    Let’s start with repository, I used the same kind of repository I created for iTraveller with some changes in creating the db4o context/container. The change is as shown below.

    public IObjectContainer Context
    {
        get
        {
            if (_context == null)
                this.CreateContext();
            return _context;
        }
    }
    
    public void CreateContext()
    {
        if (_context == null)
        {
            IEmbeddedConfiguration config = Db4oEmbedded.NewConfiguration();
            config.Common.Add(new TransparentActivationSupport());
            config.Common.Add(new TransparentPersistenceSupport());
            _context = Db4oEmbedded.OpenFile(config, DBNAME);
        }
    }
    

     

    It’s the time to do some modification in Entity level.

    using System;
    usingDb4objects.Db4o.Activation;
    usingDb4objects.Db4o.Collections;

    usingDb4objects.Db4o.TA;
    namespaceNet.Sales.Manager.Domain
    {
        public classSaleOrderHeader : IActivatable
      
    {
            [System.NonSerialized]
            privateIActivator _activator;

            publicSaleOrderHeader ()
            {
               
                this.OrderDate = DateTime.Now;
                this.SaleOrderItems = newArrayList4<SaleOrderItem>();
            }

            public int OrderId { get; set; }

            private IList<SaleOrderItem> _salesOrderItems;
            public IList<SaleOrderItem> SaleOrderItems 
            {
                get 
                {
                    this.Read();
                    return this._salesOrderItems;
                }
                private set
                {
                    this._salesOrderItems = value;
                    this.Write();
                }
            }
    
            private double _discount;
            public double Discount
            {
                get
                {
                    this.Read();
                    return _discount;
                }
                set
                {
                    this.Write();
                    this._discount = value;
                }
            }
            public void With(SaleOrderItem saleOrderItem)
            {
                if(this.SaleOrderItems==null)
                    this.SaleOrderItems = new ArrayList4<SaleOrderItem>();
                this.Read();
                this.SaleOrderItems.Add(saleOrderItem);
            }
    
    
            public void Activate(ActivationPurpose purpose)
            {
                if (this._activator != null)
                    _activator.Activate(purpose);
                
            }
    
            public void Bind(IActivator activator)
            {
                if (_activator == activator)
                    return;
                
                if (activator != null && null != _activator)
                    throw new System.InvalidOperationException();
                
                _activator = activator;
            }
    
            public void Read()
            {
                this.Activate(ActivationPurpose.Read);
            }
    
            public void Write()
            {
                this.Activate(ActivationPurpose.Write);
            }
    
        }
    }
    
    

    For simplicity I removed some functions and properties from the above class. As you can see the SalesOrderHeader is implemented IActivatable. All classes that implements IActivatable is a candidate for Transparent Activation and Transparent Persistence. IActivatable will add two methods Activate and Bind. Make sure you implement the functions as shown above. I also added two helper methods Read and Write to ease the use of Activate function. Have a look at the property Discount and see how we are using it.

    private double _discount;
    public double Discount
    {
        get
        {
            this.Read();
            return _discount;
        }
        set
        {
            this.Write();
            this._discount = value;
        }
    }
    

     

    It is as simple as that.

    If we a have collection and that needs TA and TP enabled then we have to use ArrayList4. It’s inherited from IList. In the above e.g. SaleOrderItem is a collection, see how we implemented it.

    private IList<SaleOrderItem> _salesOrderItems;
    public IList<SaleOrderItem> SaleOrderItems 
    {
        get 
        {
            this.Read();
            return this._salesOrderItems;
        }
        private set
        {
            this._salesOrderItems = value;
            this.Write();
        }
    }
    

     

    Now we have to implement IActivatable in SaleOrderItem entity as well, the approach is same as mentioned above so skipping the code.

    We are done, now db4o can handle Transparent Activation and Transparent Persistence and no need to provide any graph depth. Even if we provide Update or Activate depth db4o will ignore it.

    Note

    We don’t need to implement IActivatable in top level class if it’s not dealing with a collection. Say for e.g. we have Customer and Address class and Customer has an instance of Address. Then implement IActivatable in Address no need to implement it in Customer as by default db4o will update top level object.

    Query TA/TP enabled objects

    You can see that in my Generic Repository I query data using Lambda expression, and the query is created as shown below.

    public IList<TEntity> Find<TEntity>(Func<TEntity, bool> criteria) where TEntity : class
    {
        return this.Query<TEntity>().Where<TEntity>(criteria).ToList<TEntity>();
    }
    
    
    private IList<TEntity> Query<TEntity>() where TEntity : class
    {
        return  this.Context.Query<TEntity>();
    }
    

     

    _repository.Find<SaleOrderHeader>(soh => soh.OrderId == salesOrderNumber);
    

     

    This Query model is not working if we enable TA and TP. First or Find of SaleOrder always return null. I think I need to check with db4o devs.

    The alternative is use Query by Example (QBE) as shown below.

    public IList<TEntity> Find<TEntity>(TEntity criteria) where TEntity : class
    {
        return this.Context.QueryByExample(criteria).Cast<TEntity>().ToList<TEntity>();
    }
    
    public SaleOrderHeader getSalesOrder(int salesOrderNumber)
    {
        return _repository.First<SaleOrderHeader>(new SaleOrderHeader(salesOrderNumber));
    }
    

    You can find more details from db4o online help.

    I will be releasing the entire source to Github soon.

    Written by Sony Arouje

    August 8, 2012 at 12:44 pm

    Posted in .NET

    Tagged with , ,

    db4o A light weight Object Oriented Database

    with 8 comments

    Now a days I am spending my free time in rewriting one of my freeware for Flickr users called iTraveller. One of the design goals was the application should be scalable. My major concern was, I cannot freeze my db design before my first release because I am planning to have multiple version with added functionalities, so the db structure may change in the future. If I use conventional db’s then I have to write functionality to add or remove columns. iTraveller is not very data centric application, so managing db change scripts and writing a functionality to apply these changes when user upgrade to new version will be a big pain for me.

    First I thought of using MongoDB, because I used Mongo db in a Silverlight application. But then finally dropped that idea. I want some thing more lighter than Mongo and NoSql sort of database. I evaluated several db’s but nothing suites what I am looking for. Finally my search ends at db4o. The usage of db4o is very straight forward and very easy to learn, db4o installer comes with a very good documentation to start off with the development.

    db4o is a light weight Object Oriented database. Using db4o I can persist my entities very easily without any configuration. I can add or remove new properties to my entities without affecting the existing persisted entities.

    I created a generic repository sort of class around db4o, the same way I did one for Entity Framework. This generic repository reduced lot of my work and act as the entry point to persist my entities to db4o data file. Below is the Generic repository I created for db4o.

    using Db4objects.Db4o;
    
    public class GenericRepository:IRepository,IDisposable
    {
        private static IRepository _singleRepoInstance;
        public const string DBNAME = "Data.dat";
    
        public static IRepository GetRepositoryInstance()
        {
            if (_singleRepoInstance == null)
                _singleRepoInstance = new GenericRepository();
    
            return _singleRepoInstance;
        }
    
        IObjectContainer _context = null;
        private IObjectContainer Context
        {
            get 
            {
                if(_context==null)
                    _context = Db4oFactory.OpenFile(DBNAME);
                
                return _context;
            }
        }
    
        private IList<TEntity> Query<TEntity>() where TEntity : class
        {
            return this.Context.Query<TEntity>();
        }
    
        public IList<TEntity> GetAll<TEntity>() where TEntity : class
        {
            return this.Query<TEntity>().ToList();
        }
    
        public IList<TEntity> GetAll<TEntity>(TEntity entity) where TEntity : class
        {
            return this.Context.QueryByExample(entity).Cast<TEntity>().ToList();
        }
    
        public IList<TEntity> Find<TEntity>(Func<TEntity, bool> criteria) where TEntity : class
        {
            return this.Query<TEntity>().Where<TEntity>(criteria).ToList<TEntity>();
        }
    
        public TEntity Single<TEntity>(Func<TEntity, bool> criteria) where TEntity : class
        {
            return this.Query<TEntity>().Single<TEntity>(criteria);
        }
    
        public TEntity First<TEntity>(Func<TEntity, bool> criteria) where TEntity : class
        {
            return this.Query<TEntity>().FirstOrDefault<TEntity>(criteria);
        }
        public TEntity First<TEntity>(TEntity criteria) where TEntity : class
        {
            return this.Context.QueryByExample(criteria).Cast<TEntity>().First<TEntity>();
        }
        public void Add<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.Store(entity);
        }
    
        public void Add<TEntity>(List<TEntity> entities) where TEntity : class
        {
            foreach (TEntity entity in entities)
            {
                this.Add<TEntity>(entity);
            }
        }
    
        public void Delete<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.Delete(entity);
        }
    
        public void Update<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.Store(entity);
        }
    
        public void Dispose()
        {
            this._context.Close();
            this._context.Dispose();
            this._context = null;
        }
    }
    

    The above repository is created in singletone mode. I did that way because I noticed that opening the database file is taking some delay and wanted to avoid that delay. So I wont close the data file until iTraveller get closed.

    Let’s see how we can call the above repository to save an entity

    GenericRepository repository = GenericRepository.GetRepositoryInstance();
    repository.Add<Photo>(photos);
    

     

    Below is the way we can retrieve some entities from the data file

    IRepository repository = GenericRepository.GetRepositoryInstance();
    List<LocalCategory> categories = repository.GetAll<LocalCategory>().ToList<LocalCategory>();
     

     

    You can also retrieve data based on any condition as well. The below code will return a list of Photo object whose categoryId is 10.

    IRepository repository = GenericRepository.GetRepositoryInstance();
    return repository.Find<Photo>(p => p.Category.ID == 10).ToList<Photo>();
    
    

     

    As you can see the db4o is very easy to use light weight database without the hurdles of mapping to tables and all sort of stuffs.

    Written by Sony Arouje

    January 5, 2011 at 4:26 pm

    Posted in .NET

    Tagged with , , ,

    %d bloggers like this: