In order to support Transparent Activation
, the objects which are stored in the database need to implement the IActivatable-interface.
An object which implements the IActivatable-interface is responsible for activating itself. For this purpose the class needs a field to keep its activator. This field is only used by the transparent activation framework. Therefore it's marked as transient, to avoid that it's stored in the database.
public class Person : IActivatable
{
[NonSerialized] private IActivator activator;
Public Class Person
Implements IActivatable
<Transient()> _
Private m_activator As IActivator
Then implement the two methods of the IActivatable-interface. The bind-method binds an activator to the object. It's called by the transparent activation framework. The activate-method needs to be called before any read or write operation on the object. Since these two methods are always the same, you can move the implementation to a common super class or to a static utility class.
public void Bind(IActivator activator)
{
if (this.activator == activator)
{
return;
}
if (activator != null && null != this.activator)
{
throw new InvalidOperationException("Object can only be bound to one activator");
}
this.activator = activator;
}
public void Activate(ActivationPurpose activationPurpose)
{
if (null != activator)
{
activator.Activate(activationPurpose);
}
}
Public Sub Bind(ByVal activator As IActivator) _
Implements IActivatable.Bind
If m_activator Is activator Then
Exit Sub
End If
If activator IsNot Nothing AndAlso m_activator IsNot Nothing Then
Throw New InvalidOperationException("Object can only be bound to one activator")
End If
m_activator = activator
End Sub
Public Sub Activate(ByVal activationPurpose As ActivationPurpose) _
Implements IActivatable.Activate
If m_activator IsNot Nothing Then
m_activator.Activate(activationPurpose)
End If
End Sub
Now the important part. Every time a field of the class is accessed you need to call the activate-method with the purpose. For example in every property or other method. Probably the best way is to use only property even within the class to access fields. And the property ensures that the activate-method is called.
public string Name
{
get
{
Activate(ActivationPurpose.Read);
return name;
}
set
{
Activate(ActivationPurpose.Write);
name = value;
}
}
public override string ToString()
{
// use the getter/setter withing the class,
// to ensure the activate-method is called
return Name;
}
Public Property Name() As String
Get
Activate(ActivationPurpose.Read)
Return m_name
End Get
Set(ByVal value As String)
Activate(ActivationPurpose.Write)
m_name = value
End Set
End Property
Public Overloads Overrides Function ToString() As String
' use the getter/setter withing the class,
' to ensure the activate-method is called
Return Name
End Function
Implementing the IActivatable-interface manually for every class is repetitive and error prone. That's why this process can be automated. See "TA Enhanced Example"
The last step is to enable transparent persistence via configuration.
IEmbeddedConfiguration configuration = Db4oEmbedded.NewConfiguration(); configuration.Common.Add(new TransparentActivationSupport()); IObjectContainer container = Db4oEmbedded.OpenFile(configuration, DatabaseFileName);
Dim configuration As IEmbeddedConfiguration = Db4oEmbedded.NewConfiguration() configuration.Common.Add(New TransparentActivationSupport()) Dim container As IObjectContainer = Db4oEmbedded.OpenFile(configuration, DatabaseFileName)
Now transparent persistence is enabled. Now you can navigate the object-graph as deep as you want. The transparent activation will load the objects from the database as you need them.
using (IObjectContainer container = OpenDatabaseTA())
{
Person person = Person.PersonWithHistory();
container.Store(person);
}
using (IObjectContainer container = OpenDatabaseTA())
{
Person person = QueryByName(container, "Joanna the 10");
Person beginOfDynasty = person.Mother;
// With transparent activation enabled, you can navigate deeply
// nested object graphs. db4o will ensure that the objects
// are loaded from the database.
while (null != beginOfDynasty.Mother)
{
beginOfDynasty = beginOfDynasty.Mother;
}
Console.WriteLine(beginOfDynasty.Name);
// Updating a object doesn't requires no store call.
// Just change the objects and the call commit.
beginOfDynasty.Name = "New Name";
container.Commit();
}
Using container As IObjectContainer = OpenDatabaseTA()
Dim joanna As Person = Person.PersonWithHistory()
container.Store(joanna)
End Using
Using container As IObjectContainer = OpenDatabaseTA()
Dim joanna As Person = QueryByName(container, "Joanna the 10")
Dim beginOfDynasty As Person = joanna.Mother
' With transparent activation enabled, you can navigate deeply
' nested object graphs. db4o will ensure that the objects
' are loaded from the database.
While beginOfDynasty.Mother IsNot Nothing
beginOfDynasty = beginOfDynasty.Mother
End While
Console.WriteLine(beginOfDynasty.Name)
' Updating a object doesn't requires no store call.
' Just change the objects and the call commit.
beginOfDynasty.Name = "New Name"
container.Commit()
End Using