Migrating Between Databases

Transparent activation and persistence functionality depends on an association between an object and an object container, which is created when an activator is bound to the object. Each object allows only one activator. Typically this limitation won't show up, however there is a valid use case for it:

1)      suppose you need to copy one or more objects from one object container to another;

2)      you will retrieve the object(s) from the first object container using any suitable query syntax;

3)      optionally you can close the first object container;

4)      you will now save the object to the second object container.

If both object containers were using transparent activation or persistence - the 4-th step will throw an exception. Let's look at the case in more detail. Typical activatable class contains an activator field. When transparent activation functionality is used for the first time an object container activator will be bound to the object:

SensorPanelTA.cs: Bind
01/*Bind the class to the specified object container, create the activator*/ 02 public void Bind(IActivator activator) 03 { 04 if (_activator == activator) 05 { 06 return; 07 } 08 if (activator != null && null != _activator) 09 { 10 throw new System.InvalidOperationException(); 11 } 12 _activator = activator; 13 }
SensorPanelTA.vb: Bind
01' Bind the class to the specified object container, create the activator 02 Public Sub Bind(ByVal activator As IActivator) Implements IActivatable.Bind 03 If _activator Is activator Then 04 Return 05 End If 06 If Not (activator Is Nothing Or _activator Is Nothing) Then 07 Throw New System.InvalidOperationException() 08 End If 09 _activator = activator 10 End Sub

If bind method will be re-called with the same object container, activator parameter will always be the same. However, if another object container tries to bind the object (in our case with the store call) activator parameter will be different, which will cause an exception. (Exception will be thrown even if the first object container is already closed, as activator object still exists in the memory.) This behaviour is illustrated with the following example (SensorPanelTA class from Transparent Activation chapter is used):

TAExample.cs: TestSwitchDatabases
01private static void TestSwitchDatabases() 02 { 03 StoreSensorPanel(); 04 05 IObjectContainer firstDb = Db4oFactory.OpenFile(ConfigureTA(), FirstDbName); 06 IObjectContainer secondDb = Db4oFactory.OpenFile(ConfigureTA(), SecondDbName); 07 try 08 { 09 IObjectSet result = firstDb.QueryByExample(new SensorPanelTA(1)); 10 if (result.Count > 0) 11 { 12 SensorPanelTA sensor = (SensorPanelTA)result[0]; 13 firstDb.Close(); 14 // Migrating an object from the first database 15 // into a second database 16 secondDb.Store(sensor); 17 } 18 } 19 finally 20 { 21 firstDb.Close(); 22 secondDb.Close(); 23 } 24 }
TAExample.vb: TestSwitchDatabases
01Private Shared Sub TestSwitchDatabases() 02 StoreSensorPanel() 03 04 Dim firstDb As IObjectContainer = Db4oFactory.OpenFile(ConfigureTA(), FirstDbName) 05 Dim secondDb As IObjectContainer = Db4oFactory.OpenFile(ConfigureTA(), SecondDbName) 06 Try 07 Dim result As IObjectSet = firstDb.QueryByExample(New SensorPanelTA(1)) 08 If result.Count > 0 Then 09 Dim sensor As SensorPanelTA = DirectCast(result(0), SensorPanelTA) 10 firstDb.Close() 11 ' Migrating an object from the first database 12 ' into a second database 13 secondDb.Store(sensor) 14 End If 15 Finally 16 firstDb.Close() 17 secondDb.Close() 18 End Try 19 End Sub

The solution to this problem is simple: activator should be unbound from the object:

c#:

sensor.Bind(null);

VB:

sensor.Bind(Nothing)

Note, that the object will quit being activatable for the first object container. The following example shows the described behaviour:

TAExample.cs: TestSwitchDatabasesFixed
01private static void TestSwitchDatabasesFixed() 02 { 03 StoreSensorPanel(); 04 05 IObjectContainer firstDb = Db4oFactory.OpenFile(ConfigureTA(), FirstDbName); 06 IObjectContainer secondDb = Db4oFactory.OpenFile(ConfigureTA(), SecondDbName); 07 try 08 { 09 IObjectSet result = firstDb.QueryByExample(new SensorPanelTA(1)); 10 if (result.Count > 0) 11 { 12 SensorPanelTA sensor = (SensorPanelTA)result[0]; 13 // Unbind the object from the first database 14 sensor.Bind(null); 15 // Migrating the object into the second database 16 secondDb.Store(sensor); 17 18 19 System.Console.WriteLine("Retrieving previous query results from " 20 + FirstDbName + ":"); 21 SensorPanelTA next = sensor.Next; 22 while (next != null) 23 { 24 System.Console.WriteLine(next); 25 next = next.Next; 26 } 27 28 System.Console.WriteLine("Retrieving previous query results from " 29 + FirstDbName + " with manual activation:"); 30 firstDb.Activate(sensor, Int32.MaxValue); 31 next = sensor.Next; 32 while (next != null) 33 { 34 System.Console.WriteLine(next); 35 next = next.Next; 36 } 37 38 System.Console.WriteLine("Retrieving sensorPanel from " + SecondDbName + ":"); 39 result = secondDb.QueryByExample(new SensorPanelTA(1)); 40 next = sensor.Next; 41 while (next != null) 42 { 43 System.Console.WriteLine(next); 44 next = next.Next; 45 } 46 } 47 } 48 finally 49 { 50 firstDb.Close(); 51 secondDb.Close(); 52 } 53 }
TAExample.vb: TestSwitchDatabasesFixed
01Private Shared Sub TestSwitchDatabasesFixed() 02 StoreSensorPanel() 03 04 Dim firstDb As IObjectContainer = Db4oFactory.OpenFile(ConfigureTA(), FirstDbName) 05 Dim secondDb As IObjectContainer = Db4oFactory.OpenFile(ConfigureTA(), SecondDbName) 06 Try 07 Dim result As IObjectSet = firstDb.QueryByExample(New SensorPanelTA(1)) 08 If result.Count > 0 Then 09 Dim sensor As SensorPanelTA = DirectCast(result(0), SensorPanelTA) 10 ' Unbind the object from the first database 11 sensor.Bind(Nothing) 12 ' Migrating the object into the second database 13 secondDb.Store(sensor) 14 15 16 System.Console.WriteLine("Retrieving previous query results from " + FirstDbName + ":") 17 Dim [next] As SensorPanelTA = sensor.NextSensor 18 While [next] IsNot Nothing 19 System.Console.WriteLine([next]) 20 [next] = [next].NextSensor 21 End While 22 23 System.Console.WriteLine("Retrieving previous query results from " + FirstDbName + " with manual activation:") 24 firstDb.Activate(sensor, Int32.MaxValue) 25 [next] = sensor.NextSensor 26 While [next] IsNot Nothing 27 System.Console.WriteLine([next]) 28 [next] = [next].NextSensor 29 End While 30 31 System.Console.WriteLine("Retrieving sensorPanel from " + SecondDbName + ":") 32 result = secondDb.QueryByExample(New SensorPanelTA(1)) 33 [next] = sensor.NextSensor 34 While [next] IsNot Nothing 35 System.Console.WriteLine([next]) 36 [next] = [next].NextSensor 37 End While 38 End If 39 Finally 40 firstDb.Close() 41 secondDb.Close() 42 End Try 43 End Sub