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:
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
}
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?
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
}
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:
1public object Clone() 2
{ 3
Car test = (Car)base.MemberwiseClone(); 4
test._activator = null; 5
return test; 6
}
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