Elenesski Object Database
EODB and NEODB
The Memento Pattern Database for Unity
|
These two features are part of EODB v2.x they consist of:
Much of the documentation here is based on how EODB is being used to develop The Lost Tribes; Elenesski's space game that is currently under development.
The identity map is a really important feature that, when used, guarantees that this is one, and only one instance of an object in memory at a time. The Identity Map is always there when you open an EODB database, but isn't available unless you use it.
It works like this:
While a DBSave() with a new OID will save the OID to the map you need to do a few things to use the identity map directly.
Note, I use a global static called "GlobalData.GameDB" to reference my EODB instance. Identity map is an attribute within EODB.
private BuildingType(int aOID) : this() { new EODBDescriptor(aOID, this); GlobalData.GameDB.IdentityMap.Register(cClassName,this); } public static BuildingType Load(int aOID) { return GlobalData.GameDB.IdentityMap.Get<BuildingType>(cClassName,aOID) ?? new BuildingType(aOID); }
This is all you need to do to use the identity map. It's actually really simple to use.
When dealing with obects, it's easy to save composite objects. You simply write out all the attributes. But when you have associative or aggregate objects, you need to create relationships.
Relationships come straight out of the relational database play book.
In the above code "BuildingType(int aOID)" refers to "this()" ... this private constructor looks like this:
public BuildingType() { _Buildings = new EODBFKChildren<Building>(this,Building.Load, "Buildings"); }
I declare these two additional fields.
private EODBFKChildren<Assets.Code.Model.Building> _Buildings; public IEnumerable<Assets.Code.Model.Building> Buildings { get { return _Buildings.GetChildren(); } }
The second allows you to say "myBuildingType.Buildings.Where( ... criteria ... )" ... without it, you'd have to say "myBuildingType._Buildings.GetChildren().Where( ... criteria ... )" which is far less readable.
To save the foreign key, you write this code in your Building's Defintion method():
aDescriptor.Structure(() => TheBuildingType.OID, DATA => TheBuildingType = BuildingType.Load(DATA)); aDescriptor.AddForeignKeyReference(TheBuildingType,"Buildings");
The Structure saves the object identifier, and the second associates this Building with the identified BuildingType on the named foreign key called "Buildings". The .AddForeignKeyReference is ignored on a load.
You can also get creative with the names of your foreign keys, and say stuff like:
if ( this.BuildingHeight > 400 ) aDescriptor.AddForeignKeyReference(TheBuildingType,"TallBuildings"); else aDescriptor.AddForeignKeyReference(TheBuildingType,"ShortBuildings");
Therefore, you can get all the tall buildings with a single selection rather than having to filter later on:
_TallBuildings = new EODBFKChildren<Building>(this,Building.Load, "TallBuildings"); private EODBFKChildren<Assets.Code.Model.Building> _TallBuildings; public IEnumerable<Assets.Code.Model.Building> TallBuildings { get { return _TallBuildings.GetChildren(); } }
I would recommend against implementing "Tall" and "Short" buildings until after you have evolved your database through your development. Instead, implement it like this:
public IEnumerable<Assets.Code.Model.Building> TallBuildings { get { return _Buildings.GetChildren().Where( BUILDING => BUILDING.BuildingHeight > 400 ); } }
This way you can replace TallBuildings later on with something more efficient.