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
/**//*Bind the class to the specified object container, create the activator*/
public void Bind(IActivator activator)
{
if (_activator == activator)
{
return;
}
if (activator != null && null != _activator)
{
throw new System.InvalidOperationException();
}
_activator = activator;
}
SensorPanelTA.vb: Bind
' Bind the class to the specified object container, create the activator
Public Sub Bind(ByVal activator As IActivator) Implements IActivatable.Bind
If _activator Is activator Then
Return
End If
If Not (activator Is Nothing Or _activator Is Nothing) Then
Throw New System.InvalidOperationException()
End If
_activator = activator
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
private static void TestSwitchDatabases()
{
StoreSensorPanel();
IObjectContainer firstDb = Db4oFactory.OpenFile(ConfigureTA(), FirstDbName);
IObjectContainer secondDb = Db4oFactory.OpenFile(ConfigureTA(), SecondDbName);
try
{
IObjectSet result = firstDb.QueryByExample(new SensorPanelTA(1));
if (result.Count > 0)
{
SensorPanelTA sensor = (SensorPanelTA)result[0];
firstDb.Close();
// Migrating an object from the first database
// into a second database
secondDb.Store(sensor);
}
}
finally
{
firstDb.Close();
secondDb.Close();
}
}
TAExample.vb: TestSwitchDatabases
Private Shared Sub TestSwitchDatabases()
StoreSensorPanel()
Dim firstDb As IObjectContainer = _
Db4oFactory.OpenFile(ConfigureTA(), FirstDbName)
Dim secondDb As IObjectContainer = _
Db4oFactory.OpenFile(ConfigureTA(), SecondDbName)
Try
Dim result As IObjectSet = _
firstDb.QueryByExample(New SensorPanelTA(1))
If result.Count > 0 Then
Dim sensor As SensorPanelTA = _
DirectCast(result(0), SensorPanelTA)
firstDb.Close()
' Migrating an object from the first database
' into a second database
secondDb.Store(sensor)
End If
Finally
firstDb.Close()
secondDb.Close()
End Try
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
private static void TestSwitchDatabasesFixed()
{
StoreSensorPanel();
IObjectContainer firstDb = Db4oFactory.OpenFile(ConfigureTA(), FirstDbName);
IObjectContainer secondDb = Db4oFactory.OpenFile(ConfigureTA(), SecondDbName);
try
{
IObjectSet result = firstDb.QueryByExample(new SensorPanelTA(1));
if (result.Count > 0)
{
SensorPanelTA sensor = (SensorPanelTA)result[0];
// Unbind the object from the first database
sensor.Bind(null);
// Migrating the object into the second database
secondDb.Store(sensor);
System.Console.WriteLine("Retrieving previous query results from "
+ FirstDbName + ":");
SensorPanelTA next = sensor.Next;
while (next != null)
{
System.Console.WriteLine(next);
next = next.Next;
}
System.Console.WriteLine("Retrieving previous query results from "
+ FirstDbName + " with manual activation:");
firstDb.Activate(sensor, Int32.MaxValue);
next = sensor.Next;
while (next != null)
{
System.Console.WriteLine(next);
next = next.Next;
}
System.Console.WriteLine("Retrieving sensorPanel from " + SecondDbName + ":");
result = secondDb.QueryByExample(new SensorPanelTA(1));
next = sensor.Next;
while (next != null)
{
System.Console.WriteLine(next);
next = next.Next;
}
}
}
finally
{
firstDb.Close();
secondDb.Close();
}
}
TAExample.vb: TestSwitchDatabasesFixed
Private Shared Sub TestSwitchDatabasesFixed()
StoreSensorPanel()
Dim firstDb As IObjectContainer = _
Db4oFactory.OpenFile(ConfigureTA(), FirstDbName)
Dim secondDb As IObjectContainer = _
Db4oFactory.OpenFile(ConfigureTA(), SecondDbName)
Try
Dim result As IObjectSet = _
firstDb.QueryByExample(New SensorPanelTA(1))
If result.Count > 0 Then
Dim sensor As SensorPanelTA = _
DirectCast(result(0), SensorPanelTA)
' Unbind the object from the first database
sensor.Bind(Nothing)
' Migrating the object into the second database
secondDb.Store(sensor)
System.Console.WriteLine( _
"Retrieving previous query results from " + FirstDbName + ":")
Dim [next] As SensorPanelTA = sensor.NextSensor
While [next] IsNot Nothing
System.Console.WriteLine([next])
[next] = [next].NextSensor
End While
System.Console.WriteLine( _
"Retrieving previous query results from " + FirstDbName + _
" with manual activation:")
firstDb.Activate(sensor, Int32.MaxValue)
[next] = sensor.NextSensor
While [next] IsNot Nothing
System.Console.WriteLine([next])
[next] = [next].NextSensor
End While
System.Console.WriteLine( _
"Retrieving sensorPanel from " + SecondDbName + ":")
result = secondDb.QueryByExample(New SensorPanelTA(1))
[next] = sensor.NextSensor
While [next] IsNot Nothing
System.Console.WriteLine([next])
[next] = [next].NextSensor
End While
End If
Finally
firstDb.Close()
secondDb.Close()
End Try
End Sub
Download example code: