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: