Object Clone

Platform implementations of #clone is not compatible with TP.

Both java and .NET object implementations provide #clone method for default objects, which is enabled by implementing Cloneable/ICloneable interface. This implementation is a shallow clone, i.e. only the top-level object fields are duplicated, all the referenced(children) objects are only copied as references to the same object in the parent clone. But how it affects Transparent Persistence?

If you remember Transparent Persistence Implementation you must know that a special Activator field is used to bind an object to the object container. Consequently, the default clone will copy this Activatable field to the object's duplicate, which will produce ambiguity as the object container won't know which object should be activated for write.

Let's look how it will affect db4o in practice. We will use a usual Car class and make it cloneable. Use the following code to store a car object and it's clone:

TPCloneExample.cs: StoreCar
01private static void StoreCar() 02 { 03 File.Delete(Db4oFileName); 04 IObjectContainer container = Database(Db4oFactory.NewConfiguration()); 05 if (container != null) 06 { 07 try 08 { 09 // create a car 10 Car car = new Car("BMW", new Pilot("Rubens Barrichello")); 11 container.Store(car); 12 Car car1 = (Car)car.Clone(); 13 container.Store(car1); 14 } 15 finally 16 { 17 CloseDatabase(); 18 } 19 } 20 }
 

TPCloneExample.vb: StoreCar
01Private Shared Sub StoreCar() 02 File.Delete(Db4oFileName) 03 Dim container As IObjectContainer = Database(Db4oFactory.NewConfiguration()) 04 If container IsNot Nothing Then 05 Try 06 ' create a car 07 Dim car As New Car("BMW", New Pilot("Rubens Barrichello")) 08 container.Store(car) 09 Dim car1 As Car = DirectCast(car.Clone(), Car) 10 container.Store(car1) 11 Finally 12 CloseDatabase() 13 End Try 14 End If 15 End Sub
 

So it works for the first store, but what if we will clone an object retrieved from the database?

TPCloneExample.cs: TestClone
01private static void TestClone() 02 { 03 IConfiguration configuration = ConfigureTP(); 04 05 IObjectContainer container = Database(configuration); 06 if (container != null) 07 { 08 try 09 { 10 IObjectSet result = container.QueryByExample(new Car(null, null)); 11 ListResult(result); 12 Car car = null; 13 Car car1 = null; 14 if (result.Size() > 0) 15 { 16 car = (Car)result[0]; 17 System.Console.WriteLine("Retrieved car: " + car); 18 car1 = (Car)car.Clone(); 19 System.Console.WriteLine("Storing cloned car: " + car1); 20 container.Store(car1); 21 container.Commit(); 22 } 23 } 24 finally 25 { 26 CloseDatabase(); 27 } 28 } 29 }
TPCloneExample.vb: TestClone
01Private Shared Sub TestClone() 02 Dim configuration As IConfiguration = ConfigureTP() 03 04 Dim container As IObjectContainer = Database(configuration) 05 If container IsNot Nothing Then 06 Try 07 Dim result As IObjectSet = container.QueryByExample(New Car(Nothing, Nothing)) 08 ListResult(result) 09 Dim car As Car = Nothing 10 Dim car1 As Car = Nothing 11 If result.Size() > 0 Then 12 car = DirectCast(result(0), Car) 13 System.Console.WriteLine("Retrieved car: " + car.ToString()) 14 car1 = DirectCast(car.Clone(), Car) 15 System.Console.WriteLine("Storing cloned car: " + car1.ToString()) 16 container.Store(car1) 17 container.Commit() 18 End If 19 Finally 20 CloseDatabase() 21 End Try 22 End If 23 End Sub
 

The code above throws an exception when the cloned object is being bound to the object container. Luckily we can easily fix it by overriding #clone method and setting activator to null:

Car.cs: Clone
1public object Clone() 2 { 3 Car test = (Car)base.MemberwiseClone(); 4 test._activator = null; 5 return test; 6 }
Car.vb: Clone
1Public Function Clone() As Object Implements ICloneable.Clone 2 Dim test As Car = DirectCast(MyBase.MemberwiseClone(), Car) 3 test._activator = Nothing 4 Return test 5 End Function