Spiking with Db4O, the Repository Pattern and ASP.NET MVC

by Scott MIllett 14. February 2011 10:11

For my latest book Apress Pro Agile .NET Development with SCRUM & XP I am going be using NoSQL for part of the case study solution. So in order to get up to speed with some object databases I thought I might as well do some spiking starting with Db4O. I chose Db4O due to the excellent introduction to it from my fellow wiggler Dan Glyde.

To work with Db4O you will need to pop over to the Db4O project site and downloaded the assemblies to work with .NET, or alternatively I have included them in a Lib folder of the sample project that the code snippets in this post have come from.

Ok first things first, how am I to work with the Db4O object database? Well I need an IObjectContainer. An IObjectContainer is your gateway into the Db4O object database, it's like Entity Framework's ObjectSet or NHibernate Session. The IObjectContainer also implements the Unit of Work pattern, however I will wrap this with my own storage framework unaware framework a little later.

So I need an IObjectContainer and who should create me one? Yep you guessed it an IObjectContainerFactory.

using Db4objects.Db4o;

namespace Db40Spike.Infrastructure
{
    public interface IObjectContainerFactory
    {
        IObjectContainer create();
    }
}

The implementation of the IObjectContainerFactory looks like so...

using Db4objects.Db4o;

namespace Db40Spike.Infrastructure 
{
    public class ObjectContainerFactory : 
                                   IObjectContainerFactory
    {    
        private static IObjectServer _server;
        private static IObjectContainer _object_container;
        
        public ObjectContainerFactory(string container_path) 
        {
            if (_server == null) {
                Connect(container_path);
            }
        }

        private void Connect(string container_path) {
            _server = Db4oFactory.OpenServer(container_path, 0);            
        }
        
        public IObjectContainer create()
        {
            return _server.OpenClient();
        }     
    }
}

Ok so what is this class doing? Well it takes location of a Db4O database in the constructor, connects to it and creates a server instance. This server instance will be a singleton as we only want one because we are using it in a web scenario. Think of it as the same as a NHibernates Sessionfactory, we only want to create it once because its expensive to create (in fact its acting as our server and in a web context we need to work with it in this way). The last piece of code is the create method that will create a new IObjectContainer, this satisfies the interface contract.

Right we have got our factory working now we to store a session that will hang around for the length of a http request. I will call this an IDb4OSession. Here is it's interface...

using Db4objects.Db4o;

namespace Db40Spike.Infrastructure
{
    public interface IDb4OSession
    {        
        IObjectContainer object_container_in_session { get;}        
    }
}

...and here is the implementation.

using System;
using Db4objects.Db4o;

namespace Db40Spike.Infrastructure
{
    public class Db4OSession : IDb4OSession, IDisposable
    {        
        public Db4OSession(IObjectContainerFactory objectContainerFactory)
        {
            object_container_in_session = objectContainerFactory.create();
        }

        public IObjectContainer object_container_in_session { get; private set; }

        public void Dispose()
        {
            object_container_in_session.Close();
            object_container_in_session.Dispose();
        }
    }
}

So a very simple Db4OSession takes an instance of the IObjectContainerFactory and asks it for an IObjectContainer. When the Db4OSession object gets disposed it closes the session and in turn calls dispose on it to keep things nice and tidy.

Right that's the basics, now as my spike is following DDD I am using entities and repositories, so I have an interface for an Entity..

using System;

namespace Db40Spike.Domain
{
    public interface IEntity
    {
        Guid id { get;  }
    }
}

... and an interface for a generic repository.

using System;
using System.Collections.Generic;

namespace Db40Spike.Domain
{
    public interface IRepository<Entity> where Entity : IEntity
    {
        Entity find_by(Guid id);
        IEnumerable<Entity> find_all();
        void save(Entity entity);
    }
}

The implementation of the IRepository<Entity> repository uses the Db4O linq provider to query for entities, other than that it should be straight forward.

using System.Linq;
using Db4objects.Db4o.Linq;
using System;
using System.Collections.Generic;

namespace Db40Spike.Infrastructure {

    public class Db4ORepository<Entity> 
                               : IRepository<Entity> where Entity : IEntity
    {
        private readonly IDb4OSession _db4o_session;        

        public Db4ORepository(IDb4OSession db4o_session)
        {
            _db4o_session = db4o_session;            
        }

        public Entity find_by(Guid id)
        {
            return (from Entity entity in _db4o_session.object_container_in_session
                    select entity).SingleOrDefault(g => g.id == id);
        }

        public IEnumerable<Entity> find_all()
        {
            return (from Entity entity in _db4o_session.object_container_in_session
                    select entity).ToList();            
        }

        public void save(Entity entity)
        {
            _db4o_session.object_container_in_session.Store(entity);                 
        }        
    }
}

Notice however that I never commit the changes made to the data store i.e. I never call the Commit method on the IObjectContainer (object_container_in_session) itself, so at the moment nothing is going to change when we save an entity. Before I show you how I have implemented the unit of work pattern lets see it in action beicing used in an ASP.NET MVC controller (ignore the application specific stuff like temp data)...

public class AddPersonController : Controller
{
    private readonly IRepository<Person> _person_repository;
    private readonly IUnitOfWorkFactory _unit_of_work_factory;

    public AddPersonController(IRepository<Person> person_repository,
                                IUnitOfWorkFactory unit_of_work_factory)
    {
        _person_repository = person_repository;
        _unit_of_work_factory = unit_of_work_factory;
    }

    // ........

    [HttpPost]
    public ActionResult Create(CreatePersonViewModel create_person)
    {
        if (ModelState.IsValid)
        {
            var name = new Name(create_person.first_name,
                                create_person.last_name);
            var person_to_add = new Person(name);

            using (_unit_of_work_factory.create())
            {                
                _person_repository.save(person_to_add);
            }

            TempData.Add("Message", 
                     String.Format("New person added '{0}'", 
                                   person_to_add.name));

            return RedirectToAction("Index", "DisplayAllPeople");
        }
        else
            return View(create_person);                                        
    }
}

The controller needs an IRepository<Person> and an IUnitofWorkFactory.  The UnitOfWorkFactory creates units of work that implement disposable, when the dispose method is called i.e. when the process is outside of the using block a call to commit the changes happens, and at this stage the changes are made to the data store. Make sense? Ok maybe it will after we take a look at how I have implemented the Db4O unit of work pattern.

First we need a contract...

using System;

namespace Db40Spike.Domain
{
    public interface IUnitOfWork : IDisposable
    {   }
}

.. then an implementation.

using Db40Spike.Domain;

namespace Db40Spike.Infrastructure
{
    public class Db4OUnitOfWork : IUnitOfWork
    {
        private IDb4OSession _db4o_session;

        public Db4OUnitOfWork(IDb4OSession db4OSession)
        {
            _db4o_session = db4OSession;
        }

        public void Dispose()
        {
            _db4o_session.object_container_in_session.Commit();
        }
    }
}

Simple so far? Now for the IUnitOfWorkFactory. This has a single method to create units of work...

namespace Db40Spike.Domain
{
    public interface IUnitOfWorkFactory
    {
        IUnitOfWork create(); 
    }
}

Again the implementation isn't at all complex...

using Db40Spike.Domain;

namespace Db40Spike.Infrastructure
{
    public class Db4OUnitOfWorkFactory : IUnitOfWorkFactory
    {
        private readonly IDb4OSession _db4o_session;

        public Db4OUnitOfWorkFactory(IDb4OSession db4o_session)
        {
            _db4o_session = db4o_session;
        }

        public IUnitOfWork create()
        {
            return new Db4OUnitOfWork(_db4o_session);
        }
    }
}

One point to make about my domain entities is that I have to override the equality operators in order for db4o to retrieve the correct entity. For this I have a base domain class that all my entities inherit from...

using System;

namespace Db40Spike.Domain
{
    public class DomainBase : IEntity
    {
        public Guid id { get; protected set; }

        public bool Equals(DomainBase other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return other.id.Equals(id);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (DomainBase)) return false;
            return Equals((DomainBase) obj);
        }

        public override int GetHashCode()
        {
            return id.GetHashCode();
        }
    }
}

So to tie all of the loose ends together I used StructureMap. My RepositoryRegistery is shown below..

public class RepositoryRegistry : Registry
{
    public RepositoryRegistry()
    {
        For<IRepository<Person>>()
           .Use<Db4ORepository<Person>>();

        For<IObjectContainerFactory>().Singleton()
           .Use<ObjectContainerFactory>()
           .Ctor<string>("container_path")
           .Is(HttpContext.Current
               .Server.MapPath("~/App_Data/people.db4o"));

        For<IDb4OSession>().HttpContextScoped()
           .Use<Db4OSession>();

        For<IUnitOfWorkFactory>()
           .Use<Db4OUnitOfWorkFactory >();                        
        }
    }

I would really suggest that you take a look at the complete code on my git hub account as I find it helps looking at the full solution and all the code snippets shown above in context. Any questions or comments would be great :0)


Woot, woot - the sound of the police
Scott

Tags: , ,

Design Patterns | NoSQL

Comments are closed

About Me

Hello my name is Scott Millett, this is my blog designed to capture my thoughts on development. You can find out more info on me by checking out my linked in profile below.

View Scott Millett's profile on LinkedIn

Coming soon...

Awards

MVP Logo

ASP.NET MVP
2010-2011

Month List

Powered by BlogEngine.NET 2.0.0.36 - Eco Theme by n3o Web Designers