Autoincrement

Db4o does not deliver a field autoincrement feature, which is common in RDBMS. If your application logic requires this feature you can implement it using External Callbacks. One of the possible solutions is presented below.

We will need an object to store the last generated ID and to return a new ID on request:

IncrementedId.cs
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02/* 03 * Singleton class used to keep auotincrement information 04 * and give the next available ID on request 05 */ 06using System; 07using Db4objects.Db4o; 08 09namespace Db4objects.Db4odoc.Callbacks 10{ 11 class IncrementedId 12 { 13 private int _no; 14 private static IncrementedId _ref; 15 16 private IncrementedId() 17 { 18 _no = 0; 19 } 20 // end IncrementedId 21 22 public int GetNextID(IObjectContainer db) 23 { 24 _no++; 25 db.Set(this); 26 return _no; 27 } 28 // end increment 29 30 public static IncrementedId GetIdObject(IObjectContainer db) 31 { 32 // if _ref is not assigned yet: 33 if (_ref == null) 34 { 35 // check if there is a stored instance from the previous 36 // session in the database 37 IObjectSet os = db.Get(typeof(IncrementedId)); 38 if (os.Size() > 0) 39 _ref = (IncrementedId)os.Next(); 40 } 41 42 if (_ref == null) 43 { 44 // create new instance and store it 45 Console.WriteLine("Id object is created"); 46 _ref = new IncrementedId(); 47 db.Set(_ref); 48 } 49 return _ref; 50 } 51 // end getIdObject 52 } 53}
IncrementedId.vb
01' Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02 03' Singleton class used to keep auotincrement information 04' and give the next available ID on request 05 06Imports System 07Imports Db4objects.Db4o 08Namespace Db4objects.Db4odoc.Callbacks 09 10 Class IncrementedId 11 Private _no As Integer 12 Private Shared _ref As IncrementedId 13 14 Private Sub New() 15 _no = 0 16 End Sub 17 ' end New 18 19 Public Function GetNextID(ByVal db As IObjectContainer) As Integer 20 System.Math.Min(System.Threading.Interlocked.Increment(_no), _no - 1) 21 db.Set(Me) 22 Return _no 23 End Function 24 ' end GetNextID 25 26 Public Shared Function GetIdObject(ByVal db As IObjectContainer) As IncrementedId 27 ' if _ref is not assigned yet: 28 If _ref Is Nothing Then 29 ' check if there is a stored instance from the previous 30 ' session in the database 31 Dim os As IObjectSet = db.Get(GetType(IncrementedId)) 32 If os.Size > 0 Then 33 _ref = CType(os.Next, IncrementedId) 34 End If 35 End If 36 If _ref Is Nothing Then 37 ' create new instance and store it 38 Console.WriteLine("Id object is created") 39 _ref = New IncrementedId 40 db.Set(_ref) 41 End If 42 Return _ref 43 End Function 44 ' end GetIdObject 45 46 End Class 47End Namespace

This object generates the simplest ID, which is an autoincremented integer value. You can add your own algorithm to generate more sophisticated ID sequences, like ABC0001DEF.

When you use external callbacks you are not limited to a single object: a callback can apply to any group of objects Thus you can create a sequence of classes sharing the same autoincrement. To distinguish the objects, which will have an autoincremented field, we will use an abstract (MustInherit in VB) class:

CountedObject.cs
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02/* 03 * This class is used to mark classes that need to get an autoincremented ID 04 */ 05namespace Db4objects.Db4odoc.Callbacks 06{ 07 abstract class CountedObject 08 { 09 protected int _id; 10 11 public int Id 12 { 13 get 14 { 15 return _id; 16 } 17 set 18 { 19 _id = value; 20 } 21 } 22 } 23}
CountedObject.vb
01' Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02'This class is used to mark classes that need to get an autoincremented ID 03Namespace Db4objects.Db4odoc.Callbacks 04 05 MustInherit Class CountedObject 06 Protected _id As Integer 07 08 Public Property Id() As Integer 09 Get 10 Return _id 11 End Get 12 Set(ByVal value As Integer) 13 _id = value 14 End Set 15 End Property 16 End Class 17End Namespace

Each object extending CountedObject will get an autoincremented ID. For example:

TestObject.cs
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02namespace Db4objects.Db4odoc.Callbacks 03{ 04 class TestObject: CountedObject 05 { 06 string _name; 07 08 public TestObject(string name) { 09 _name = name; 10 } 11 12 public override string ToString() { 13 return _name+"/"+_id; 14 } 15 } 16}
TestObject.vb
01' Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com 02Namespace Db4objects.Db4odoc.Callbacks 03 04 Class TestObject 05 Inherits CountedObject 06 Private _name As String 07 08 Public Sub New(ByVal name As String) 09 _name = name 10 End Sub 11 12 Public Overloads Overrides Function ToString() As String 13 Return _name + "/" + _id.ToString() 14 End Function 15 End Class 16End Namespace

It is only left to register the callback with the creating() event:

AutoIncExample.cs: RegisterCallback
1public static void RegisterCallback() 2 { 3 IObjectContainer db = OpenContainer(); 4 IEventRegistry registry = EventRegistryFactory.ForObjectContainer(db); 5 // register an event handler, which will assign autoincremented IDs to any 6 // object extending CountedObject, when the object is created 7 registry.Creating += new CancellableObjectEventHandler(OnCreating); 8 }
AutoIncExample.cs: OnCreating
01private static void OnCreating(object sender, CancellableObjectEventArgs args) 02 { 03 IObjectContainer db = OpenContainer(); 04 object obj = args.Object; 05 // only for the objects extending the CountedObject 06 if (obj is CountedObject) 07 { 08 ((CountedObject)obj).Id = GetNextId(db); 09 } 10 }
AutoIncExample.cs: GetNextId
01private static int GetNextId(IObjectContainer db) 02 { 03 // this function retrieves the next available ID from 04 // the IncrementedId object 05 IncrementedId r = IncrementedId.GetIdObject(db); 06 int nRoll; 07 nRoll = r.GetNextID(db); 08 09 return nRoll; 10 }

AutoIncExample.vb: RegisterCallback
1Public Shared Sub RegisterCallback() 2 Dim db As IObjectContainer = OpenContainer() 3 ' register an event handler, which will assign autoincremented IDs to any 4 ' object extending CountedObject, when the object is created 5 Dim registry As IEventRegistry = EventRegistryFactory.ForObjectContainer(db) 6 AddHandler registry.Creating, AddressOf OnCreating 7 End Sub
AutoIncExample.vb: OnCreating
1Private Shared Sub OnCreating(ByVal sender As Object, ByVal args As CancellableObjectEventArgs) 2 Dim db As IObjectContainer = OpenContainer 3 Dim obj As Object = args.Object 4 ' only for the objects extending the CountedObject 5 If TypeOf obj Is CountedObject Then 6 CType(obj, CountedObject).Id = GetNextId(db) 7 End If 8 End Sub

AutoIncExample.vb: GetNextId
1Private Shared Function GetNextId(ByVal db As IObjectContainer) As Integer 2 ' this function retrieves the next available ID from 3 ' the IncrementedId object 4 Dim r As IncrementedId = IncrementedId.GetIdObject(db) 5 Dim nRoll As Integer 6 nRoll = r.GetNextID(db) 7 Return nRoll 8 End Function

You can test the results with the following code:

AutoIncExample.cs: StoreObjects
01public static void StoreObjects() 02 { 03 IObjectContainer db = OpenContainer(); 04 TestObject test; 05 test = new TestObject("FirstObject"); 06 db.Set(test); 07 test = new TestObject("SecondObject"); 08 db.Set(test); 09 test = new TestObject("ThirdObject"); 10 db.Set(test); 11 }
AutoIncExample.cs: RetrieveObjects
1public static void RetrieveObjects() 2 { 3 IObjectContainer db = OpenContainer(); 4 IObjectSet result = db.Get(new TestObject(null)); 5 ListResult(result); 6 }
AutoIncExample.vb: StoreObjects
01Public Shared Sub StoreObjects() 02 Dim db As IObjectContainer = OpenContainer 03 Dim test As TestObject 04 test = New TestObject("FirstObject") 05 db.Set(test) 06 test = New TestObject("SecondObject") 07 db.Set(test) 08 test = New TestObject("ThirdObject") 09 db.Set(test) 10 End Sub
AutoIncExample.vb: RetrieveObjects
1Public Shared Sub RetrieveObjects() 2 Dim db As IObjectContainer = OpenContainer 3 Dim result As IObjectSet = db.Get(New TestObject(Nothing)) 4 ListResult(result) 5 End Sub

Please, note that the suggested implementation cannot be used in a multithreaded environment. In such environment you will have to make sure that the IncrementedId class can only be saved to the database once, and that 2 threads cannot independently and simultaneously increment IncrementedId counter.