Jul 25, 2009

What's wrong with this code(Part XI) - Answer

In this post I presented the following code.

using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using System.Linq;

class Item
{
public string Name { get; set; }
public Item Father { get; set ; }
}

public class Test
{
 private const string DatabaseFile = "wwwtc1.odb";
 private static void Main()
 {
     Item item1 = new Item { Name = "Adriano" };
     using (var db = Db4oEmbedded.OpenFile(DatabaseFile))
     {
         db.Store(item1);
     }

     using (var db = Db4oEmbedded.OpenFile(DatabaseFile))
     {
         Item item2 = new Item {Name = "Carol",  Father = item1 };
         db.Store(item2);
     }
 }
}

And asked what was wrong with it (and commenter "tehlike" got it :). 


In line 20 we close the database (technically, the using statement will call IDisposable.Dispose() method on the object container which in turn will close it), reopen it at line 22 and finally add a new object (item2) at line 25.  

Take a closer look in the program and you'll see that item2 references item1 which was stored in the database previously. As the documentation states (here and here), db4o uses a reference system to "memorize" which objects are known to be stored in the database (relying on object identity when checking if a given object is already present in this reference system). 

When a database is opened its reference system is empty, so when storing item2 db4o will store item1 again (after all we re-opened the database, so there are no objects in the reference system and both objects are considered as "new", i.e, need to be inserted into the database),  i.e, now we have 2 instances of class Item with the same contents in the database.

What can a developer do to avoid this behavior? Easy, just let the just opened object container know that item1 has already been persisted by retrieving it before referencing it from item2 :) 

using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using System.Linq;

class Item
{
  public string Name { get; set; }
  public Item Father { get; set ; }
}

public class Test
{
 private const string DatabaseFile = "wwwtc1.odb";
 private static void Main()
 {
     Item item1 = new Item { Name = "Adriano" };
     using (var db = Db4oEmbedded.OpenFile(DatabaseFile))
     {
         db.Store(item1);
     }

     using (var db = Db4oEmbedded.OpenFile(DatabaseFile))
     {
         item1 = (from Person p in db where p.Name == "Adriano").Single();
         Item item2 = new Item {Name = "Carol",  Father = item1 };
         db.Store(item2);
     }
 }
}

Another possible solution (with less performance impact) would be to not close/reopen the database so item1 would be kept in the reference system and db4o would understand that it does not need to store this object again.

using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using System.Linq;

class Item
{
  public string Name { get; set; }
  public Item Father { get; set ; }
}

public class Test
{
  private const string DatabaseFile = "wwwtc1.odb";
  private static void Main()
  {
     Item item1 = new Item { Name = "Adriano" };
     using (var db = Db4oEmbedded.OpenFile(DatabaseFile))
     {
         db.Store(item1);
         Item item2 = new Item {Name = "Carol",  Father = item1 };
         db.Store(item2);
     }
  }
}


Have fun!

Adriano

Jul 16, 2009

What's wrong with this code(Part XI) ?

Since I joined db4o I'm actively following our forums and trying to help developers to get their questions / issues answered.

Starting with this post I want to present the most recurring topics (don't know exactly how many) in the format of "What's wrong with this code".

But before we start you may be wondering where are the other parts (since this is titled as Part XI, where are parts from I to X). Well, they do exists in the portuguese posts (titled as "O que esta errado com este código (Parte ?)") so I decided to continue with the numbering schema ;)

Ok, for this first one, take a look in the following code:
using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using System.Linq;

class Item 
{
 public string Name { get; set; }
 public Item Father { get; set ; }
}

public class Test
{
    private const string DatabaseFile = "wwwtc1.odb";
    private static void Main()
    {
        Item item1 = new Item { Name = "Adriano" };
        using (var db = Db4oEmbedded.OpenFile(DatabaseFile))
        {
           db.Store(item1);
        }
  
        using (var db = Db4oEmbedded.OpenFile(DatabaseFile))
        {
          Item item2 = new Item {Name = "Carol",  Father = item1 };
          db.Store(item2);
        }  
    }
}
If you have done some work with db4o you probably will easily spot the problem.

I'll provide the answer in a future post.

Have fun!