You are here: Basics Operations & Concepts > Update Concept > Transparent Persistence > Object Clone

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 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:

VB.NET c#