Upon further examination, I determined the ‘Unique Field Constraint’ problem was fixed. It occurred while focusing on another issue concerning the retrieval of objects using the IObjectContainer#Ext().GetByUUID method. I explicitly separated (isolated) processing of ‘Transient’ vs ‘Persistent’ object references.
Maybe I should go back to the beginning, and examine how these problems materialized and the revision cycle that occurred to find work arounds. The purpose of this project was to determine the functionality of Db4Objects database with particular emphasis on the following:
1. Integrity – restricting duplicate items by using ‘Unique Field Constraints’ to define single or composite primary keys and differentiate between ‘adding’ new data and ‘updating’ existing data within a complex graph.
2. Relations – add and remove soft links using UUIDs between two(2) class objects within a complex graph thus allowing multiple views without creating duplicate datasets. Both ‘Uni-directional’ and ‘Bi-directional’ links.
3. Performance – check process time duration to ‘add’ new items, ‘update’ existing items or ‘retrieve’ items using a small(00), medium(0000) or large(000000) datasets.
The first revision used a single graph as follows:
/* Define 'Car-Driver' graph hierarchy.
[+] Car [Class reference - Root]
|->[.] Model [String Indexed - PKey.1]
|->[.] Year [String Indexed - SKey]
+->[+] Drivers [List<Driver> - FKey]
|->[.] Name [String Indexed - PKey.2]
|->[.] Points [Int32 Indexed - SKey]
+->[-] Cars [List<Car> - Closure]
This graph failed because ‘duplicate’ items were created when ‘updating’ the database using IObjectContainer#set and #commit methods. I also tried to restrict ‘adding’ new items using ‘Unique Field Constraints’ but it failed due to multiple constraints set within the same graph. (ie: PKey.1 & PKey.2)
The second revision used a ‘Root’ list to encapsulate the ‘Cars’ as a generic list collection as follows:
/* Define 'ListCar' graph hierarchy.
[+] ListCar [Class reference - Root]
|->[.] Name [String(1) = “DbCars”]
+->[+] Cars [List<Car> - FKey]
|->[.] Model [String Indexed - PKey.1]
|->[.] Year [String Indexed - SKey]
+->[+] Drivers [List<Driver> - FKey]
|->[.] Name [String Indexed - PKey.2]
|->[.] Points [Int32 Indexed - SKey]
+->[-] Cars [List<Car> - Closure]
This graph eliminated the ‘duplicate’ items when ‘updating’ the database. Thus the ListCar class is similar to a relational table and the Cars generic list collection represents the rows. The ‘Unique Field Constraint’ still failed.
The third revision used two(2) graphs converting the previous graph to 3rd normal form, thus eliminating the multiple constraints within the same graph as follows:
/* Define 'ListCar' graph hierarchy.
[+] ListCar [Class reference - Root]
|->[.] Name [String(1) = “DbCars”]
+->[+] Cars [List<Car> - Fkeys.1]
|->[.] Model [String Indexed - PKey.1]
|->[.] Year [String Indexed - SKey]
+->[.] Drivers [List<Driver> - FKey.2]
/* Define 'ListDriver' graph hierarchy.
[+] ListDriver [Class reference - Root]
|->[.] Name [String (1) = “DbDrivers”]
+->[+] Drivers [List<Driver> - FKeys.2]
|->[.] Name [String Indexed - PKey.2]
|->[.] Points [Int32]
+->[.] Cars [List<Car> - FKey.1]
These graphs solved the multiple ‘Unique Field Constraint’ allowing ‘updating’ of existing items but introduced another anomaly. The side effect occurred when I mixed the Cars collection which is ‘Transient’ with the ListCar class which is ‘Persistent’. The AddDataCarDriver method was dependent on four other methods AddDbType<ListCar>, UpdateDbType<ListCar>, AddDataCar and AddDataDriver which intermixed ‘Transient’ and ‘Persistent’ reference instances. This caused the try-catch block to reject the ‘updating’ of the items. This was a very difficult problem to ferret out. The lesson learned is to explicitly separate processing of ‘Transient’ vs ‘Persistent’ references. I now have a set of routines for ‘Transient’ processing as follows:
AddDataCar, AddDataCarDriver, AddDataDriver and AddDataDriverCar.
Also a set of routines for ‘Persistent’ processing as follows:
AddDbType<..>, UpdateDbType<..>, AddDbCar, AddDbCarDriver, AddDbDriver and AddDbDriverCar.
I still have to test for composite unique field keys (ie: FirstName & LastName) in the ListDriver graph. I suspect that a redundant field will be needed, that combines the FirstName and LastName into one field which is designated as a ‘Unique Field Constraint’.
The final revision used both inter-related 3rd normal graphs, but instead of creating reference copies between graphs as in revision three, I introduced soft links using the Db4oUUID concept as follows:
/* Define 'ListCar' graph hierarchy.
[+] ListCar [Class reference - Root]
|->[.] Name [String(1) = “DbCars”]
+->[+] Cars [List<Car> - UUIDKey.1]
|->[.] Model [String Indexed - PKey.1]
|->[.] Year [String Indexed - SKey]
|->[.] DriverUUIDs [List<Db4oUUID> - UUIDKeys.2]
+->[.] Drivers {Transient} [List<Driver> - FKeys]
/* Define 'ListDriver' graph hierarchy.
[+] ListDriver [Class reference - Root]
|->[.] Name [String one only ='DbDrivers']
+->[+] Drivers [List<Driver> - UUIDKey.2]
|->[.] Name [String Indexed - PKey.1]
|->[.] Points [Int32]
|->[.] CarUUIDs [List<Db4oUUID> - UUIDKeys.1
+->[.] Cars {Transient} [List<Car> - FKey.2]
This revision requires an explicit configuration to generate Db4oUUIDs for Cars and Drivers as follows:
Db4oFactory.Configure().ObjectClass(typeof(Car)).GenerateUUIDs(true);
Db4oFactory.Configure().ObjectClass(typeof(Driver)).GenerateUUIDs(true);
Also two(2) new routines (AddDbCarDriverRelation & AddDbDriverCarRelation) are required to add the soft links to the designated inter-related list collections. The Db4oUUIDs are retrieved as follows:
Db4oUUID driverUUID = db.Ext().GetObjectInfo(dbDriver).GetUUID();
Db4oUUID carUUID = db.Ext().GetObjectInfo(dbCar).GetUUID();
They are subsequently tested if they exist, then added to the Cars or Drivers list collection and updated as follows:
dbListCar.Cars[ndxCar].DriverUUIDs.Add(driverUUID);
UpdateDbType<ListCar>(dbListCar, db)
or
dbListDriver.Drivers[ndxDriver].CarUUIDs.Add(carUUID);
UpdateDbType<ListDriver>(dbListDriver, db);
A problem occurred upon retrieval using the IObjectContainer#Ext().GetByUUID(object) method. You must explicitly apply IObjectContainer#Activate(object,depth) otherwise the object returned is empty! Hopefully the ‘Transparent Activation’ will fix this exception to the global activation configuration.
For me, this has been a very interesting learning experience on how to use Db4Object database and will continue into the future. Hopefully the ‘Fast Collections’ project will provide greatly inproved performance and allow the developer to explicitly manipulate very large lists which require careful consumption of memory.
I would appreciate any suggestion on how you would navigated through this maze. There were moments when I felt like I was blind folded in the presents of a very large object. Upon removal of the blind fold I discovered an elephant. This subsequent vision impacted and redirected my future endevours.
Regards, John Sealey
Fountain Valley CA