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 Implementationyou 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 private static void StoreCar() { File.Delete(Db4oFileName); IObjectContainer container = Database(Db4oFactory.NewConfiguration()); if (container != null) { try { // create a car Car car = new Car("BMW", new Pilot("Rubens Barrichello")); container.Store(car); Car car1 = (Car)car.Clone(); container.Store(car1); } finally { CloseDatabase(); } } }
TPCloneExample.vb: StoreCar Private Shared Sub StoreCar() File.Delete(Db4oFileName) Dim container As IObjectContainer = _ Database(Db4oFactory.NewConfiguration()) If container IsNot Nothing Then Try ' create a car Dim car As New Car("BMW", New _ Pilot("Rubens Barrichello")) container.Store(car) Dim car1 As Car = DirectCast(car.Clone(), Car) container.Store(car1) Finally CloseDatabase() End Try End If End Sub
So it works for the first store, but what if we will clone an object retrieved from the database?
TPCloneExample.cs: TestClone private static void TestClone() { IConfiguration configuration = ConfigureTP(); IObjectContainer container = Database(configuration); if (container != null) { try { IObjectSet result = container.QueryByExample(new Car(null, null)); ListResult(result); Car car = null; Car car1 = null; if (result.Size() > 0) { car = (Car)result[0]; System.Console.WriteLine("Retrieved car: " + car); car1 = (Car)car.Clone(); System.Console.WriteLine("Storing cloned car: " + car1); container.Store(car1); container.Commit(); } } finally { CloseDatabase(); } } }
TPCloneExample.vb: TestClone Private Shared Sub TestClone() Dim configuration As IConfiguration = ConfigureTP() Dim container As IObjectContainer = Database(configuration) If container IsNot Nothing Then Try Dim result As IObjectSet = container.QueryByExample( _ New Car(Nothing, Nothing)) ListResult(result) Dim car As Car = Nothing Dim car1 As Car = Nothing If result.Size() > 0 Then car = DirectCast(result(0), Car) System.Console.WriteLine("Retrieved car: " _ + car.ToString()) car1 = DirectCast(car.Clone(), Car) System.Console.WriteLine("Storing cloned car: " _ + car1.ToString()) container.Store(car1) container.Commit() End If Finally CloseDatabase() End Try End If 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 public object Clone() { Car test = (Car)base.MemberwiseClone(); test._activator = null; return test; }
Car.vb: Clone Public Function Clone() As Object Implements ICloneable.Clone Dim test As Car = DirectCast(MyBase.MemberwiseClone(), Car) test._activator = Nothing Return test End Function
Download example code: