Sunday, November 14, 2010

MVC 2.0 with DB4O

Object-Oriented Databases

The aim of this blog is to firstly give an introduction of what an object-oriented database is, showing how to install DB4O, any shortcomings and finally to give an example in a C# .NET 4 MVC 2 shopping cart project.

What is an Object-Oriented Database?

An object-oriented database is a database model (OODBMS), unlike relational database management systems which hold data in relational rows and columns the OODBMS holds information as objects in the same way as object-oriented programming.

OODBMS is useful when used with object-oriented programming as no translation needs to take place between objects in code and objects in a database, the same objects can be referenced in each.

Why choose OODBMS over RDBMS?

OODBMS are faster than RDBMS and can be programmed without making changes to the full system.

As navigation with OODBMS is done with pointers data access is done very efficiently.

On the negative, OODBMS are optimised for specific search routes and will tend to be slower for general-purpose queries than relational however direct object references may be maintained to allow for both types of access.

So what OODBMS should I choose?

This depends on the language you are programming in, I’m pro open source and the language I am writing for this example is in .Net I have selected the highly rated db4o: http://www.db4o.com/about/productinformation/db4o/

This o-o database has libraries for both Java and .NET, for a full list and comparison of OODBMS see this wiki link: http://en.wikipedia.org/wiki/Comparison_of_object_database_management_systems

Installation guide of db4o for .NET

Download the production release from the following location: http://developer.db4o.com/Downloads.aspx

The following steps are for db4o-7.12-net35.msi

Setup type: Typical
Accept the default installation location
Go though the defaults and click Finish to complete the installation

If you have more than one version of Visual Studio installed you get the choice of plugin to install.

‘To proceed with the installation of the Object Manager Enterprise plugin for VS please choose the appropriate shortcut from your db4objects Start menu’

The plugin does not work with Microsoft Visual Web Developer – you will need the fully blown version of the Visual Studio.

The tutorial automatically loads on completion of installation from here code examples can be obtained for processes such as opening databases to CRUD to LINQ and SODA queries.

The only DLL you need to build against is C:\Program Files\db4o\db4o-7.12\bin\net-3.5\ Db4objects.Db4o.dll.

Issues
Cache
Db40 has its own caching, the default is ‘CachingStorage’, db4o uses a LRU/Q2 caching strategy. http://developer.db4o.com/Documentation/Reference/db4o-8.0/net35/reference/index_Left.html#CSHID=configuration%2Ffile%2Fstorage%2Fisolated_storage.htmStartTopic=Content%2Fconfiguration%2Ffile%2Fstorage%2Fisolated_storage.htmSkinName=RedVersant

Load balancing
http://developer.db4o.com/Documentation/Reference/db4o-8.0/net35/reference/index_Left.html#CSHID=configuration%2Ffile%2Fstorage%2Fisolated_storage.htmStartTopic=Content%2Fconfiguration%2Ffile%2Fstorage%2Fisolated_storage.htmSkinName=RedVersant

Locking
http://www.gamlor.info/wordpress/?p=779

Serialized objects
As soon as objects are serialized the object-identity is lost, this is typical in web-scenarios or web-services, there are a number of different ids, one of which is “ID-Field On Classes With a ID-Generator”.

The project

I developed this MVC 2.0 project using Visual Studio 2010 Ultimate.

The source is available on GitHub.

It is assumed you have working knowledge of the MVC framework.

The only DLLs I referenced from my application are:

Db4objects.Db4o
Db4objects.Db4o.Linq

The second was included as I used Linq queries on the object database.

To keep this example simple I have not addressed the issues of load balancing and isolated storage.

I have created a new class in the Models folder: StoreDB4O

In the class I define the yap file – this is the file DB4O uses to store the database.


   1:  readonly static string StoreYapFileName = Path.Combine( 
   2:    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), 
   3:    "store.yap"); 

...and obtain it using the following method:

   1:  private static IObjectContainer GetDB()
   2:  {
   3:    returnDb4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(), StoreYapFileName);
   4:  }



In order to get test data in to the project I have created a method called LoadTestData(), this is called from Application_Start() in Global.asax and removes the content of the database:


   1:  File.Delete(StoreYapFileName);


And stores the sample data in Category and Product objects:


   1:  IObjectContainer db = GetDB();
   2:  try
   3:  {
   4:      Category category = new Category { CategoryId = 1, Name = "Bikes" };
   5:      StoreCategory(db, category);




   1:  private static void StoreCategory(IObjectContainer db, Category category)
   2:  {
   3:      db.Store(category);
   4:  }

There is nothing special about the objects that are stored in the database.

   1:  namespace MvcDB4O.Models
   2:  {
   3:      public class Category
   4:      {
   5:          public int CategoryId { get; set; }
   6:          public string Name { get; set; }
   7:      }
   8:  }

In addition to storing data in object I have implemented methods that return data using Linq queries as example of which returns a list of products by category name:

   1:  public static IEnumerable<Product> GetProductsByCategoryName(string name)
   2:          {
   3:              IObjectContainer db = GetDB();
   4:   
   5:              try
   6:              {
   7:                  return new List<Product>(from Product p in db
   8:                                            where p.Category.Name == name
   9:                                            orderby p.Name
  10:                                            select p).Distinct(new ProductByNameEquality());
  11:   
  12:              }
  13:              finally
  14:              {
  15:                  db.Close();
  16:              }
  17:          }

One important thing to mention is to always return a copy of the object from the database otherwise the connection of the database will be required to be open when accessing data from the objects.

Another gotcha is that when updating existing data, the same object will be required from the database within the same session i.e. In this example, the basket has the count decremented and the basket is then placed back in the object database, if this was done in two separate sessions with the database a second basket will be added to the database and the existing one will also exist.


   1:  public static IEnumerable<Product> GetProductsByCategoryName(string name)
   2:          {
   3:              IObjectContainer db = GetDB();
   4:   
   5:              try
   6:              {
   7:                  return new List<Product>(from Product p in db
   8:                                            where p.Category.Name == name
   9:                                            orderby p.Name
  10:                                            select p).Distinct(new ProductByNameEquality());
  11:   
  12:              }
  13:              finally
  14:              {
  15:                  db.Close();
  16:              }
  17:          }

The MVC structure here is nothing new, the Home\Index.aspx shows the categories and a link to the StoreController Browse method:


   1:          <% foreach (MvcDB4O.Models.Category category in Model.Categories)
   2:             { %>         
   3:          <li>
   4:              <%: Html.ActionLink(category.Name, "Browse", "Store", new { category = category.Name }, null)%>
   5:          </li>     
   6:          <% } %>
   7:   
   8:          public ActionResult Browse(string category)
   9:          {
  10:              // Retrieve Category and Products from database     
  11:              var storeBrowseViewModel = new StoreBrowseViewModel()
  12:                  { 
  13:                      Category = StoreDB4O.GetCategory(category),
  14:                      Products = StoreDB4O.GetProductsByCategoryName(category)
  15:                  };
  16:              return View(storeBrowseViewModel);
  17:          }

The View Model is populated and used in the Store\Browse.aspx page.

   1:      public class StoreBrowseViewModel
   2:      {
   3:          public Category Category { get; set; }
   4:          public IEnumerable<Product> Products { get; set; }
   5:      }
   6:   
   7:          <% foreach (MvcDB4O.Models.Product product in Model.Products)
   8:             { %>         
   9:          <li>
  10:              <%: Html.ActionLink(product.Name, "Details", "Store", new { id = product.ProductId }, null)%>
  11:          </li>     
  12:          <% } %>


I hope this has served to show how to connect to an object database in particular DB4O using MVC.

Please ask any questions you may have and I welcome your comments.







1 comment: