Retrieve an object in one DB4O session, store in another ('disconnected scenario')
I am trying to figure out how to keep an object useable between client sessions in DB4O. From what I understand, once a client session is closed, the object no longer resides in any cache and despite the fact that I have a valid UUID, I cannot call Store on it without causing a duplicate to be inserted. I searched for a way to manually re-add it to the cache but there is no such mecha开发者_如何转开发nism. Re-retrieving it will force me to copy over all the values from the now useless object.
Here's the above paragraph in code:
Person person = new Person() { FirstName = "Howdoyu", LastName = "Du" };
Db4oUUID uuid;
// Store the new person in one session
using (IObjectContainer client = server.OpenClient())
{
client.Store(person);
uuid = client.Ext().GetObjectInfo(person).GetUUID();
}
// Guy changed his name, it happens
person.FirstName = "Charlie";
using (var client = server.OpenClient())
{
// TODO: MISSING SOME WAY TO RE-USE UUID HERE
client.Store(person); // will create a new person, named charlie, instead of changing Mr. Du's first name
}
The latest version of Eloquera supports these scenarios, either through an [ID] attribute or via Store(uid, object).
Any thoughts?
This functionality is indeed missing in db4o =(. That makes db4o very difficult to use in many scenarios.
You basically have to write your own reattach method by coping all attributes over. Maybe a library like Automapper can help, but in the end you have to do it yourself.
Another question is if you really want to use the db4o UUIDs to identify an object. db4o UUIDs are huge and not a well known type. I personally would prefer regular .NET GUIDs.
By the way: There's the db4o .Bind() method, which binds a object to an existing id. However it hardly does what you really want. I guess that you want to store changes made to an object. Bind basically replaces the object and breaks the object graph. For example if you have a partially loaded objects and then bind it, you loose references to objects. So .Bind is not usable.
Okay, Gamlor's response about the db4o IExtContainer.Bind() method pointed me to the solution. Please note that this solution is only valid in very specific situations where access to the DB is tightly controlled, and no external queries can retrieve object instances.
Warning: This solution is dangerous. It can fill your database with all kinds of duplicates and junk objects, because it replaces the object and doesn't update its values, therefore breaking any references to it. Click here for a complete explanation.
UPDATE: Even in tightly controlled scenarios, this can cause endless headaches (like the one I'm having now) for anything other than a flat object with value type properties only (string, int, etc.). Unless you can design your code to retrieve, edit and save objects in a single db4o connection, then I recommend not to use db4o at all.
Person person = new Person() { FirstName = "Charles", LastName = "The Second" };
Db4oUUID uuid;
using (IObjectContainer client = server.OpenClient())
{
// Store the new object for the first time
client.Store(person);
// Keep the UUID for later use
uuid = client.Ext().GetObjectInfo(person).GetUUID();
}
// Guy changed his name, it happens
person.FirstName = "Lil' Charlie";
using (var client = server.OpenClient())
{
// Get a reference only (not data) to the stored object (server round trip, but lightweight)
Person inactiveReference = (Person) client.Ext().GetByUUID(uuid);
// Get the temp ID for this object within this client session
long tempID = client.Ext().GetID(inactiveReference);
// Replace the object the temp ID points to
client.Ext().Bind(person, tempID);
// Replace the stored object
client.Store(person);
}
精彩评论