<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <link rel="stylesheet" type="text/css" href="docs.css"> <!--[if gte IE 5]> <link href="docs_ie.css" rel="stylesheet" type="text/css"> <![endif]--> </head> <body><div id="pagecontainer"><table><tr><td width="5"> </td><td><a name="Deep"></a><br> <a name="outline189"></a><br><h1>8. Deep graphs</h1><br> We have already seen how db4o handles object associations, but our running example is still quite flat and simple, compared to real-world domain models. In particular we haven't seen how db4o behaves in the presence of recursive structures. We will emulate such a structure by replacing our history list with a linked list implicitely provided by the SensorReadout class.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>using System;<br> namespace Db4odoc.Tutorial.F1.Chapter6<br> { <br> public abstract class SensorReadout<br> {<br> DateTime _time;<br> Car _car;<br> string _description;<br> SensorReadout _next;<br> <br> protected SensorReadout(DateTime time, Car car, string description)<br> {<br> _time = time;<br> _car = car;<br> _description = description;<br> _next = null; <br> }<br> <br> public Car Car<br> {<br> get<br> {<br> return _car;<br> }<br> }<br> <br> public DateTime Time<br> {<br> get<br> {<br> return _time;<br> }<br> }<br> <br> public SensorReadout Next<br> {<br> get<br> {<br> return _next;<br> }<br> }<br> <br> public void Append(SensorReadout sensorReadout)<br> {<br> if (_next == null)<br> {<br> _next = sensorReadout;<br> }<br> else<br> {<br> _next.Append(sensorReadout);<br> }<br> }<br> <br> public int CountElements()<br> {<br> return (_next == null ? 1 : _next.CountElements() + 1);<br> }<br> <br> override public string ToString()<br> {<br> return string.Format("{0} : {1} : {2}", _car, _time, _description);<br> }<br> }<br> }<br> </code></td></tr></table> <br> <br> Our car only maintains an association to a 'head' sensor readout now.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>using System;<br> namespace Db4odoc.Tutorial.F1.Chapter6<br> {<br> public class Car<br> {<br> string _model;<br> Pilot _pilot;<br> SensorReadout _history;<br> <br> public Car(string model)<br> {<br> _model = model;<br> _pilot = null;<br> _history = null;<br> }<br> <br> public Pilot Pilot<br> {<br> get<br> {<br> return _pilot;<br> }<br> <br> set<br> {<br> _pilot = value;<br> }<br> }<br> <br> public string Model<br> {<br> get<br> {<br> return _model;<br> }<br> }<br> <br> public SensorReadout GetHistory()<br> {<br> return _history;<br> }<br> <br> public void Snapshot()<br> { <br> AppendToHistory(new TemperatureSensorReadout(<br> DateTime.Now, this, "oil", PollOilTemperature()));<br> AppendToHistory(new TemperatureSensorReadout(<br> DateTime.Now, this, "water", PollWaterTemperature()));<br> AppendToHistory(new PressureSensorReadout(<br> DateTime.Now, this, "oil", PollOilPressure()));<br> }<br> protected double PollOilTemperature()<br> {<br> return 0.1*CountHistoryElements();<br> }<br> <br> protected double PollWaterTemperature()<br> {<br> return 0.2*CountHistoryElements();<br> }<br> <br> protected double PollOilPressure()<br> {<br> return 0.3*CountHistoryElements();<br> }<br> <br> override public string ToString()<br> {<br> return string.Format("{0}[{1}]/{2}", _model, _pilot, CountHistoryElements());<br> }<br> <br> private int CountHistoryElements()<br> {<br> return (_history == null ? 0 : _history.CountElements());<br> }<br> <br> private void AppendToHistory(SensorReadout readout)<br> {<br> if (_history == null)<br> {<br> _history = readout;<br> }<br> else<br> {<br> _history.Append(readout);<br> }<br> }<br> }<br> }<br> </code></td></tr></table> <br> <br> <ul> <a name="outline190"></a><br><h2>8.1. Storing and updating</h2><br> No surprises here.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// storeCar<br> Pilot pilot = new Pilot("Rubens Barrichello", 99);<br> Car car = new Car("BMW");<br> car.Pilot = pilot;<br> db.Store(car);</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4odoc.f1.chapter6.DeepExample", "storeCar")' /></td></tr></table> <br> <br> Now we would like to build a sensor readout chain. We already know about the update depth trap, so we configure this first.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// takeManySnapshots<br> IEmbeddedConfiguration config = Db4oEmbedded.NewConfiguration();<br> config.Common.ObjectClass(typeof(Car)).CascadeOnUpdate(true);<br> using(IObjectContainer db = Db4oEmbedded.OpenFile(config, YapFileName))<br> {<br> IObjectSet result = db.QueryByExample(typeof(Car));<br> Car car = (Car)result.Next();<br> for (int i = 0; i < 5; i++)<br> {<br> car.Snapshot();<br> }<br> db.Store(car);<br> }</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4odoc.f1.chapter6.DeepExample", "takeManySnapshots")' /></td></tr></table> <br> <br> <a name="outline191"></a><br><h2>8.2. Retrieving</h2><br> Now that we have a sufficiently deep structure, we'll retrieve it from the database and traverse it.<br> <br> First let's verify that we indeed have taken lots of snapshots.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// retrieveAllSnapshots<br> IObjectSet result = db.QueryByExample(typeof(SensorReadout));<br> while (result.HasNext())<br> {<br> Console.WriteLine(result.Next());<br> }</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4odoc.f1.chapter6.DeepExample", "retrieveAllSnapshots")' /></td></tr></table> <br> <br> All these readouts belong to one linked list, so we should be able to access them all by just traversing our list structure.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// retrieveSnapshotsSequentially<br> IObjectSet result = db.QueryByExample(typeof(Car));<br> Car car = (Car)result.Next();<br> SensorReadout readout = car.GetHistory();<br> while (readout != null)<br> {<br> Console.WriteLine(readout);<br> readout = readout.Next;<br> }</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4odoc.f1.chapter6.DeepExample", "retrieveSnapshotsSequentially")' /></td></tr></table> <br> <br> Ouch! What's happening here?<br> <br> <a name="Activation"></a><br> <ul> <a name="outline192"></a><br><h2>8.2.1. Activation depth</h2><br> Deja vu - this is just the other side of the update depth issue.<br> <br> db4o cannot track when you are traversing references from objects retrieved from the database. So it would always have to return 'complete' object graphs on retrieval - in the worst case this would boil down to pulling the whole database content into memory for a single query.<br> <br> This is absolutely undesirable in most situations, so db4o provides a mechanism to give the client fine-grained control over how much he wants to pull out of the database when asking for an object. This mechanism is called <em>activation depth</em> and works quite similar to our familiar update depth.<br> <br> The default activation depth for any object is 5, so our example above runs into nulls after traversing 5 references.<br> <br> We can dynamically ask objects to activate their member references. This allows us to retrieve each single sensor readout in the list from the database just as needed.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// retrieveSnapshotsSequentiallyImproved<br> IObjectSet result = db.QueryByExample(typeof(Car));<br> Car car = (Car)result.Next();<br> SensorReadout readout = car.GetHistory();<br> while (readout != null)<br> {<br> db.Activate(readout, 1);<br> Console.WriteLine(readout);<br> readout = readout.Next;<br> }</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4odoc.f1.chapter6.DeepExample", "retrieveSnapshotsSequentiallyImproved")' /></td></tr></table> <br> <br> Note that 'cut' references may also influence the behavior of your objects: in this case the length of the list is calculated dynamically, and therefor constrained by activation depth.<br> <br> Instead of dynamically activating subgraph elements, you can configure activation depth statically, too. We can tell our SensorReadout class objects to cascade activation automatically, for example.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// retrieveSnapshotsSequentiallyCascade<br> IEmbeddedConfiguration config = Db4oEmbedded.NewConfiguration();<br> config.Common.ObjectClass(typeof(TemperatureSensorReadout))<br> .CascadeOnActivate(true);<br> using(IObjectContainer db = Db4oEmbedded.OpenFile(config, YapFileName))<br> {<br> IObjectSet result = db.QueryByExample(typeof(Car));<br> Car car = (Car)result.Next();<br> SensorReadout readout = car.GetHistory();<br> while (readout != null)<br> {<br> Console.WriteLine(readout);<br> readout = readout.Next;<br> }<br> }</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4odoc.f1.chapter6.DeepExample", "retrieveSnapshotsSequentiallyCascade")' /></td></tr></table> <br> <br> You have to be very careful, though. Activation issues are tricky. Db4o provides a wide range of configuration features to control activation depth at a very fine-grained level. You'll find those triggers in Db4objects.Db4o.Config.Configuration and the associated IObjectClass and IObjectField classes.<br> <br> Don't forget to clean up the database.<br> <br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>// deleteAll<br> IObjectSet result = db.QueryByExample(typeof(Object));<br> foreach (object item in result)<br> {<br> db.Delete(item);<br> }</code></td><td class="lg" align="left" valign="bottom" width=43><input type='button' class='button' value='Run' onclick='window.external.RunExample("com.db4odoc.f1.Util", "deleteAll")' /></td></tr></table> <br> <br> </ul> <a name="outline193"></a><br><h2>8.3. Conclusion</h2><br> Now we should have the tools at hand to work with arbitrarily complex object graphs. But so far we have only been working forward, hoping that the changes we apply to our precious data pool are correct. What if we have to roll back to a previous state due to some failure? In the <a href="Transactions.html#Transactions">next chapter</a> we will introduce the db4o transaction concept.<br> <br> <a name="outline194"></a><br><h2>8.4. Full source</h2><br> <table width="100%" cellpadding="3" cellspacing="0" border="0"><tr><td class="lg"> <code>using System;<br> using System.IO;<br> using Db4objects.Db4o;<br> using Db4objects.Db4o.Config;<br> namespace Db4odoc.Tutorial.F1.Chapter6<br> {<br> public class DeepExample : Util<br> {<br> readonly static string YapFileName = Path.Combine(<br> Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),<br> "formula1.yap"); <br> <br> public static void Main(string[] args)<br> {<br> File.Delete(YapFileName);<br> using(IObjectContainer db = Db4oEmbedded.OpenFile(YapFileName))<br> {<br> StoreCar(db);<br> }<br> TakeManySnapshots();<br> using(IObjectContainer db = Db4oEmbedded.OpenFile(YapFileName))<br> {<br> RetrieveAllSnapshots(db);<br> }<br> using(IObjectContainer db = Db4oEmbedded.OpenFile(YapFileName))<br> {<br> RetrieveSnapshotsSequentially(db);<br> RetrieveSnapshotsSequentiallyImproved(db);<br> }<br> RetrieveSnapshotsSequentiallyCascade();<br> }<br> <br> public static void StoreCar(IObjectContainer db)<br> {<br> Pilot pilot = new Pilot("Rubens Barrichello", 99);<br> Car car = new Car("BMW");<br> car.Pilot = pilot;<br> db.Store(car);<br> }<br> <br> public static void TakeManySnapshots()<br> {<br> IEmbeddedConfiguration config = Db4oEmbedded.NewConfiguration();<br> config.Common.ObjectClass(typeof(Car)).CascadeOnUpdate(true);<br> using(IObjectContainer db = Db4oEmbedded.OpenFile(config, YapFileName))<br> {<br> IObjectSet result = db.QueryByExample(typeof(Car));<br> Car car = (Car)result.Next();<br> for (int i = 0; i < 5; i++)<br> {<br> car.Snapshot();<br> }<br> db.Store(car);<br> }<br> }<br> <br> public static void RetrieveAllSnapshots(IObjectContainer db)<br> {<br> IObjectSet result = db.QueryByExample(typeof(SensorReadout));<br> while (result.HasNext())<br> {<br> Console.WriteLine(result.Next());<br> }<br> }<br> <br> public static void RetrieveSnapshotsSequentially(IObjectContainer db)<br> {<br> IObjectSet result = db.QueryByExample(typeof(Car));<br> Car car = (Car)result.Next();<br> SensorReadout readout = car.GetHistory();<br> while (readout != null)<br> {<br> Console.WriteLine(readout);<br> readout = readout.Next;<br> }<br> }<br> <br> <br> public static void RetrieveSnapshotsSequentiallyCascade()<br> {<br> IEmbeddedConfiguration config = Db4oEmbedded.NewConfiguration();<br> config.Common.ObjectClass(typeof(TemperatureSensorReadout))<br> .CascadeOnActivate(true);<br> using(IObjectContainer db = Db4oEmbedded.OpenFile(config, YapFileName))<br> {<br> IObjectSet result = db.QueryByExample(typeof(Car));<br> Car car = (Car)result.Next();<br> SensorReadout readout = car.GetHistory();<br> while (readout != null)<br> {<br> Console.WriteLine(readout);<br> readout = readout.Next;<br> }<br> }<br> }<br> <br> public static void RetrieveSnapshotsSequentiallyImproved(IObjectContainer db)<br> {<br> IObjectSet result = db.QueryByExample(typeof(Car));<br> Car car = (Car)result.Next();<br> SensorReadout readout = car.GetHistory();<br> while (readout != null)<br> {<br> db.Activate(readout, 1);<br> Console.WriteLine(readout);<br> readout = readout.Next;<br> }<br> }<br> <br> }<br> }<br> </code></td></tr></table> <br> <br><br><div id="footer"><p align="center">Do you have any questions, suggestions or feedback? Ask your questions in the <a href="http://developer.db4o.com/Forums.aspx" target=_top>db4o forums</a>. Join the <a href="http://developer.db4o.com" target=_top>db4o community</a> for addional resources and news.<br><br><a href="http://www.db4o.com/" target=_top><small>www.db4o.com</small></a></p>.</div><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br></td></tr></table></div></body></html>