Elenesski Object Database  EODB and NEODB
The Memento Pattern Database for Unity
22 Local - Identity and Relationships

Identity and Relationships

These two features are part of EODB v2.x they consist of:

  • Fowler's Identity Map
  • Implementing Related Objects (aka Relationships)

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.

Identity Map

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:

IdentityMap.png
EODB, Identity Map

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.

  1. Make your normal EODB Loading constructor private
  2. After loading your EODB object, register it with the Identity map.
  3. Create a static that finds the object by first looking in the identity map, calling the constructor if not found:

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.

Relationships

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.

  1. We use the term "relationship" to refer to a connection between objects. They encompass associative and aggregated objects.
  2. We use the term "foreign key" to refer to the parent object in the relationship. In a relational database a "foreign key" refers to only one type of table. In EODB, you can use foreign keys for any kind of relationship. The rule is, that the relationship must have a name, so I name mine prefixed by "FK".

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.