Understanding Lazy loading
Lazy Loading is a powerful feature for working with data. Suppose you have a planet with some towns in it, and each town has a set of buildings. Without lazy loading, you have to load data for the towns for a planet, and load data for buildings within a town to get access to the buildings. This platform supports that kind of approach.
Lazy Loading allows you to abstract that decision making away and have the system automatically load the towns for a planet if you reference towns. If the data is already loaded, you simply reference the data. If it's not loaded, the Lazy Load stops, loads the data into memory, and continues once it's there.
DISCLAIMER: It's important now to tell you that Lazy Loading incurs a penalty however. By using LazyLoading you get all the children instances whether you need them or not. So if you know which town to work with, you might want to extract the buildings for that town (without lazy loading) to be database efficient. It's a trade off between ease of use and data efficiency. In a lot of cases, Lazy Loading is the best option if the amount of data your game needs is relatively small. I recommend you read up on the pitfalls of Lazy Loading before integrating it into your architecture!
Lazy Loading allows you to abstract that decision making away and have the system automatically load the towns for a planet if you reference towns. If the data is already loaded, you simply reference the data. If it's not loaded, the Lazy Load stops, loads the data into memory, and continues once it's there.
DISCLAIMER: It's important now to tell you that Lazy Loading incurs a penalty however. By using LazyLoading you get all the children instances whether you need them or not. So if you know which town to work with, you might want to extract the buildings for that town (without lazy loading) to be database efficient. It's a trade off between ease of use and data efficiency. In a lot of cases, Lazy Loading is the best option if the amount of data your game needs is relatively small. I recommend you read up on the pitfalls of Lazy Loading before integrating it into your architecture!
Implementing and using Lazy Children
Implementing a Lazy Child has a declaration, initialization and an optional method that is used when new instances are added to the child list. In the end LazyLoadChildren implements IList<T>, ICollection, and IEnumerable<T> so it gives you a bunch of list, collection and Enumerable features.
Here is the code, I'll explain it below:
public class Planet : BaseClass {
public LazyLoadChildren<Town,Planet,TownTab> Towns { get; private set; }
public Planet( int aOID , string aPlanetName ) {
// Other initialization
Towns = new LazyLoadChildren<Town, Planet, TownTab>( this , "ThePlanetOID" , AddTown );
}
void AddTown (Planet aMaster,Town aChild) {
aChild.FieldOrder = aMaster.References.Count+1;
}
}
DECLARATION defines the parts necessary to load a child. It's a complicated generic that uses three generic parameters, the domain class of the child, the domain class of the parent, and the table where child instances are selected from:
public LazyLoadChildren<Town,Planet,TownTab> Towns { get; private set; }
INITIALIZATION constructs the object and identifies the foreign key need to tie the child to the parent and provides an optional method. The optional method is logic that is applied when new instances are added to the list. It's only called whenever you add a if this is a new unsaved instance. In this example, "ThePlanetOID" is used in "TownTab" to identify which towns are on the Planet.
Towns = new LazyLoadChildren<Town, Planet, TownTab>( this , "ThePlanetOID" , AddTown );
OPTIONAL METHOD is used when you need special logic to be instances after they are added or inserted to the list.
Here is the code, I'll explain it below:
public class Planet : BaseClass {
public LazyLoadChildren<Town,Planet,TownTab> Towns { get; private set; }
public Planet( int aOID , string aPlanetName ) {
// Other initialization
Towns = new LazyLoadChildren<Town, Planet, TownTab>( this , "ThePlanetOID" , AddTown );
}
void AddTown (Planet aMaster,Town aChild) {
aChild.FieldOrder = aMaster.References.Count+1;
}
}
DECLARATION defines the parts necessary to load a child. It's a complicated generic that uses three generic parameters, the domain class of the child, the domain class of the parent, and the table where child instances are selected from:
public LazyLoadChildren<Town,Planet,TownTab> Towns { get; private set; }
INITIALIZATION constructs the object and identifies the foreign key need to tie the child to the parent and provides an optional method. The optional method is logic that is applied when new instances are added to the list. It's only called whenever you add a if this is a new unsaved instance. In this example, "ThePlanetOID" is used in "TownTab" to identify which towns are on the Planet.
Towns = new LazyLoadChildren<Town, Planet, TownTab>( this , "ThePlanetOID" , AddTown );
OPTIONAL METHOD is used when you need special logic to be instances after they are added or inserted to the list.
Implementing Lazy masters
Lazy Masters are a little easier to implement. It consists of a two part declaration and initialization:
public class Town {
private LazyLoadMaster<Planet,PlanetTab> _ThePlanet;
public Item ThePlanet{ get { return _ThePlanet.Value; } set { _ThePlanet.Value = value; } }
public Town(int aOID, string aName, float aLatitude , float aLongitude, int aThePlanetOID) : base(aOID) {
OID = aOID;
Name= aName;
Latitude = aLatitude;
Longitude= aLongitude;
_ThePlanet= new LazyLoadMaster<Planet,PlanetTab>( aThePlanetOID);
}
}
DECLARATION creates a reference to a master, which is identify through Master.Value. However the use of the property allows us to reference Value directly, which is why it's two parts:
private LazyLoadMaster<Planet,PlanetTab> _ThePlanet;
public Item ThePlanet{ get { return _ThePlanet.Value; } set { _ThePlanet.Value = value; } }
INITIALIZATION is simply a call to identify the master of the child.
_ThePlanet= new LazyLoadMaster<Planet,PlanetTab>( aThePlanetOID);
It doesn't actually attempt to load the master until you reference the "Value" field.
public class Town {
private LazyLoadMaster<Planet,PlanetTab> _ThePlanet;
public Item ThePlanet{ get { return _ThePlanet.Value; } set { _ThePlanet.Value = value; } }
public Town(int aOID, string aName, float aLatitude , float aLongitude, int aThePlanetOID) : base(aOID) {
OID = aOID;
Name= aName;
Latitude = aLatitude;
Longitude= aLongitude;
_ThePlanet= new LazyLoadMaster<Planet,PlanetTab>( aThePlanetOID);
}
}
DECLARATION creates a reference to a master, which is identify through Master.Value. However the use of the property allows us to reference Value directly, which is why it's two parts:
private LazyLoadMaster<Planet,PlanetTab> _ThePlanet;
public Item ThePlanet{ get { return _ThePlanet.Value; } set { _ThePlanet.Value = value; } }
INITIALIZATION is simply a call to identify the master of the child.
_ThePlanet= new LazyLoadMaster<Planet,PlanetTab>( aThePlanetOID);
It doesn't actually attempt to load the master until you reference the "Value" field.
LAZY EXAMPLES linq and lamda
The following are code snippets showing you how to use Planets and Towns that are set up as lazy masters and lazy children.
myPlanet.Towns.Add( new Town(0,"Bitzel",51.2,-19.8,myPlanet.OID) );
List<Town> BTowns = myPlanet.Towns.Where(TOWN=>TOWN.Name.StartsWith("B")).ToList();
if ( BTowns.Count > 0 ) {
BTowns[0].Name = BTowns[0].Name + " (" + BTowns[0].ThePlanet.Name + ")"
BTowns[0].Save(); // Save the new town to the database
}
myPlanet.Towns.Add( new Town(0,"Bitzel",51.2,-19.8,myPlanet.OID) );
List<Town> BTowns = myPlanet.Towns.Where(TOWN=>TOWN.Name.StartsWith("B")).ToList();
if ( BTowns.Count > 0 ) {
BTowns[0].Name = BTowns[0].Name + " (" + BTowns[0].ThePlanet.Name + ")"
BTowns[0].Save(); // Save the new town to the database
}