In order to support TP, the persistent objects 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 "TP Enhancement"
The last step is to enable transparent persistence via configuration.
IEmbeddedConfiguration configuration = Db4oEmbedded.NewConfiguration(); configuration.Common.Add(new TransparentPersistenceSupport()); IObjectContainer container = Db4oEmbedded.OpenFile(configuration, DatabaseFileName);
Dim configuration As IEmbeddedConfiguration = Db4oEmbedded.NewConfiguration() configuration.Common.Add(New TransparentPersistenceSupport()) Dim container As IObjectContainer = Db4oEmbedded.OpenFile(configuration, DatabaseFileName)
Now transparent persistence is enabled. You have to store the object only once initially. After that, changes are automatically stored on the commit call.
using (IObjectContainer container = OpenDatabaseTP()) { Person person = Person.PersonWithHistory(); container.Store(person); } using (IObjectContainer container = OpenDatabaseTP()) { Person person = QueryByName(container, "Joanna the 10"); Person beginOfDynasty = person.Mother; // With transparent persistence 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 (IObjectContainer container = OpenDatabaseTP()) { Person person = QueryByName(container, "New Name"); // The changes are stored, due to transparent persistence Console.WriteLine(person.Name); }
Using container As IObjectContainer = OpenDatabaseTP() Dim joanna As Person = Person.PersonWithHistory() container.Store(joanna) End Using Using container As IObjectContainer = OpenDatabaseTP() Dim joanna As Person = QueryByName(container, "Joanna the 10") Dim beginOfDynasty As Person = joanna.Mother ' With transparent persistence 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 Using container As IObjectContainer = OpenDatabaseTP() Dim joanna As Person = QueryByName(container, "New Name") ' The changes are stored, due to transparent persistence Console.WriteLine(joanna.Name) End Using