Distributable Containers

ObjectContainer and ChangeSetContainer

ObjectContainers are serializable objects containing one or more root objects and their child objects. The container can be connected to another PersistenceManager. That way it is possible to transfer objects to an OfflinePersistenceManager or to another PersistenceManager which is able to save these objects again in another data store.

Communication between PersistenceManager and OfflinePersistenceManager

The communication between a PersistenceManager and an OfflinePersistanceManager works like this:

Step 1:

The PersistenceManager supplies an ObjectContainer if the function GetObjectContainer() is called.

Code sample:

PersistenceManager pm = new PersistenceManager();
var list = from e in pm.Objects<Employee>() where e.... select e;
ObjectContainer oc = pm.GetObjectContainer(list);

or:

ObjectContainer oc = new ObjectContainer();
oc.Formatter = new NdoJsonFormatter();  // from package ndo.jsonformatter
list.Select(e=>oc.AddObject(e));

Step 2:

The container is serialized.

Code sample:

oc.Serialize(...);  // string or stream

Step 3:

On the client side the container is deserialized and connected to an OfflinePersistenceManager using the method MergeObjectContainer().

Code sample:

OfflinePersistenceManager opm = new OfflinePersistenceManager();
ObjectContainer oc = new ObjectContainer();
oc.Deserialize(...);  // string or stream
opm.MergeObjectContainer(oc);

The steps 1..3 can be omitted if the OfflinePersistenceManager is only used to create new objects.

Step 4:

Now objects can be changed, deleted or created on the offline side. Changed objects can be retrieved by calling GetChangeSet().The result is a ChangeSetContainer. Like the ObjectContainer, the ChangeSetContainer is serializable too.

Code sample:

ChangeSetContainer csc = opm.GetChangeSet();
csc.Serialize(...); // string or stream

Step 5:

The ChangeSetContainer can be connected to a PersistenceManager using the method MergeObjectContainer(). The data can be stored in the database by calling Save().

Code sample:

ChangeSetContainer csc = new ChangeSetContainer();
csc.Deserialize(...); // string or stream
pm.MergeObjectContainer(csc);
pm.Save();

Picture 1: The complete communication cycle

If .NET Remoting is used, the explicit serialization and deserialisation of the ObjectContainer and ChangeSetContainer to strings or streams is not necessary. The containers can be directly used as method parameters or return values.

Communication between two PersistenceManager Instances

This communication becomes necessary to store the same objects in different databases; for example in web applications.

The following steps are necessary:

Step 1:

The PersistenceManager supplies an ObjectContainer by calling GetObjectContainer(). In this situation it makes sense to use the flag SerializationFlags.MarkAsTransient() as shown below. That way the object can be stored again in the target PersistenceManager.

Code sample:

PersistenceManager pm = new PersistenceManager();
Employee e = pm.Objects<Employee>().SingleOrDefault(emp=>emp.Name == "Mirko");
ObjectContainer oc = pm.GetObjectContainer(e);

Step 2:

The container gets serialized:

oc.Serialize(...); // string or stream.

Step 3:

On the client side the container is deserialized and merged to another PersistenceManager using MergeObjectContainer(). If the container contains Objects marked as transient they are made persistent and stored as new objects. Note that in Step 1 we made sure to mark our object as transient. But if an object had an oid before, the oid will be transferred along with the object. NDO tries to use that existing oid while storing the new object. That way the new object has the same id as the object in the original database. Reusing oids works only with client generated Id values. Guids are very well suited for that scenario.

Code sample:

PersistenceManager pm = new PersistenceManager();
ObjectContainer oc = new ObjectContainer();
oc.Deserialize(...);  // string or stream
pm.MergeObjectContainer(oc);
pm.Save();

Picture 2: The picture shows which PersistenceManager types create which container types and which container types are accepted as parameters for the MergeObjectContainer method of a certain PersistenceManager type.

Serialization

For web services it works well to use the string serialization:

string s = oc.Serialize();
...
ObjectContainer oc = new ObjectContainer();
oc.Deserialize(s);

For transferring objects to files the Stream variant can be used:

FileStream fs = new FileStream(name, FileMode.Create, FileAccess.Write);
oc.Serialize(fs);
...
FileStream fs = new FileStream(name, FileMode.Open, FileAccess.Read);
ObjectContainer oc = new ObjectContainer();
oc.Deserialize(fs);
For Remoting ObjectContainers can be submitted as parameter and result values of a method:
PersistenceManager pm = new PersistenceManager();
ChangeSetContainer csc = RemoteObject.DoSomething(pm.GetObjectContainer(...));
pm.MergeChangeSet(csc);
pm.Save();