1 #region Copyright ©  2003-2008 Richard Beauchamp <rbeauchamp@gmail.com>
   2 
   3 /*
   4 * DomainObjects for .NET
   5 * Copyright ©  2003-2008 Richard Beauchamp
   6 *
   7 * This library is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU Lesser General Public
   9 * License as published by the Free Software Foundation; either
  10 * version 2.1 of the License, or (at your option) any later version.
  11 *
  12 * This library is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * Lesser General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU Lesser General Public
  18 * License along with this library; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  20 */
  21 
  22 #endregion
  23 
  24 using System.Collections.Generic;
  25 using DomainObjects.Diagnostics;
  26 using DomainObjects.Facade.Command;
  27 using DomainObjects.Facade.Test;
  28 using DomainObjects.Test.Domain;
  29 using DomainObjects.Test.TestType.Enum;
  30 using NUnit.Framework;
  31 
  32 namespace DomainObjects.Test.TestFixture
  33 {
  34   /// <summary>
  35   /// Tests the prefetch feature of DomainObjects. 'Prefetching' means retrieving all
  36   /// objects in an object graph via a single database query rather than lazy-loading
  37   /// the object graph.
  38   /// </summary>
  39   [TestFixture]
  40   public class PrefetchTests : DomainObjectsTestFixture
  41   {
  42     [Test]
  43     public void PrefetchNonNullableCollectionOneLevelDeep()
  44     {
  45       // For unit test purposes, instantiate a CommandMonitor
  46       // to track how many commands are executed against the database
  47       CommandMonitor commandMonitor = new CommandMonitor();
  48 
  49       // Create a instance of a query
  50       Query<Person> query = new Query<Person>();
  51 
  52       // Find the instances of Person where the
  53       // first name is 'tom' and the last name is 'thumbs'
  54       query.Where.AddEqualTo(Types.Person.Field._firstname, "tom");
  55       query.Where.AddEqualTo(Types.Person.Field._lastname, "thumbs");
  56 
  57       // Prefetch items in the roles collection
  58       query.AddPrefetch(Types.Person._roles);
  59 
  60       // Execute the query
  61       Person person = query.FindObject();
  62 
  63       // Assert that we found the instance of Person
  64       Assert.IsNotNull(person, "Found instance of Person with first name 'tom' and last name 'thumbs'.");
  65 
  66       // If the Roles were not prefetched, this is when
  67       // DomainObjects would lazy load them.
  68       Assert.AreEqual(2, person.Roles.Count, "Tom has two roles.");
  69 
  70       // We are asserting here that only one query was made to the
  71       // database during this unit test. In other words, when we invoked
  72       // 'person.Roles.Count' above, the roles collection was already loaded.
  73       Assert.AreEqual(1, commandMonitor.NumberOfCommandsExecuted, "Only one query was made to the database. I.e., the Roles were not lazy loaded.");
  74     }
  75 
  76     [Test]
  77     public void PrefetchNonNullableRelationshipsTwoLevelsDeep()
  78     {
  79       // Track how many commands are executed against the database
  80       CommandMonitor commandMonitor = new CommandMonitor();
  81 
  82       Query<Person> query = new Query<Person>();
  83 
  84       Criteria whereCriteria = new Criteria();
  85       whereCriteria.AddEqualTo(Types.Person.Field._firstname, "tom");
  86       whereCriteria.AddEqualTo(Types.Person.Field._lastname, "thumbs");
  87       query.Where = whereCriteria;
  88 
  89       // Prefetch items in the roles and project relationships
  90       query.AddPrefetch(Types.Person._roles._project);
  91 
  92       Person person = query.FindObject();
  93 
  94       Assert.AreEqual(2, person.Roles.Count, "Tom has two roles.");
  95 
  96       Assert.IsNotNull(person.Roles[0].Project, "The first role is related to a project");
  97       Assert.AreEqual("DomainObjects", person.Roles[0].Project.Title, "The title of the project related to the first role is 'DomainObjects'.");
  98 
  99       Assert.IsNotNull(person.Roles[1].Project, "The second role is related to a project");
 100       Assert.AreEqual("SODA", person.Roles[1].Project.Title, "The title of the project related to the second role is 'SODA'.");
 101 
 102       Assert.AreEqual(1, commandMonitor.NumberOfCommandsExecuted, "Only one query was made to the database. I.e., the Roles and Project relationships were not lazy loaded.");
 103     }
 104 
 105     /// <summary>
 106     /// Tests that a left-outer join is generated if the prefetched
 107     /// relationship is nullable.
 108     /// </summary>
 109     [Test]
 110     public void PrefetchNullableRelationshipOneLevelDeep()
 111     {
 112       // Create a root object without a relationship to a SubType instance
 113       RootObject rootObject = RootObject.Create(LegalValue.VALUE_ONE);
 114 
 115       PersistenceFacade.Persist(rootObject);
 116 
 117       int primaryKey = (int) rootObject.PrimaryKey[0];
 118 
 119       ClearAppDomainAndContextCache();
 120 
 121       Query<RootObject> query = new Query<RootObject>();
 122 
 123       // Retrieve the root object that we just created
 124       query.Where.AddEqualTo(Types.RootObject.Field._primaryKey, primaryKey);
 125 
 126       // Prefetch the SubTypes related to the root object
 127       // (No rows exist)
 128       query.AddPrefetch(Types.RootObject._subTypes);
 129 
 130       // Track how many commands are executed against the database
 131       CommandMonitor commandMonitor = new CommandMonitor();
 132 
 133       RootObject rootObjectFromDatabase = query.FindObject();
 134 
 135       Assert.IsNotNull(rootObjectFromDatabase, "The root object was found.");
 136 
 137       Assert.AreEqual(0, rootObjectFromDatabase.SubTypes.Count, "The root object was found is not related to any sub types.");
 138 
 139       Assert.AreEqual(1, commandMonitor.NumberOfCommandsExecuted, "Only one query was made to the database. I.e., the Roles and Project relationships were not lazy loaded.");
 140     }
 141 
 142     /// <summary>
 143     /// Tests that children from different relationship descriptors
 144     /// within the same parent can be prefetched.
 145     /// </summary>
 146     [Test]
 147     public void PrefetchMutlipleRelationshipsWithinSingleParent()
 148     {
 149       // Track how many commands are executed against the database
 150       CommandMonitor commandMonitor = new CommandMonitor();
 151 
 152       Query<Role> query = new Query<Role>();
 153 
 154       Criteria whereCriteria = new Criteria();
 155       whereCriteria.AddEqualTo(Types.Role._person.Field._firstname, "tom");
 156       whereCriteria.AddEqualTo(Types.Role._person.Field._lastname, "thumbs");
 157       query.Where = whereCriteria;
 158 
 159       // Prefetch items in the project relationship
 160       // and person relationship
 161       query.AddPrefetch(Types.Role._project);
 162       query.AddPrefetch(Types.Role._person);
 163 
 164       List<Role> roles = query.FindObjectSet();
 165 
 166       Assert.AreEqual(2, roles.Count, "Tom has two roles.");
 167 
 168       Assert.IsNotNull(roles[0].Project, "The first role is related to a project");
 169       Assert.AreEqual("DomainObjects", roles[0].Project.Title, "The title of the project related to the first role is 'DomainObjects'.");
 170       Assert.IsNotNull(roles[0].Person, "The first role is related to a person");
 171       Assert.AreEqual("tom", roles[0].Person.Firstname, "The first name of the person related to the first role is 'tom'.");
 172 
 173       Assert.IsNotNull(roles[1].Project, "The second role is related to a project");
 174       Assert.AreEqual("SODA", roles[1].Project.Title, "The title of the project related to the second role is 'SODA'.");
 175       Assert.IsNotNull(roles[1].Person, "The second role is related to a person");
 176       Assert.AreEqual("tom", roles[1].Person.Firstname, "The first name of the person related to the second role is 'tom'.");
 177 
 178       Assert.AreEqual(1, commandMonitor.NumberOfCommandsExecuted, "Only one query was made to the database. I.e., the Roles and Project relationships were not lazy loaded.");
 179     }
 180 
 181     [Test]
 182     public void PrefetchWithSubClassInRelationshipPath()
 183     {
 184       // Create a set of classes where the base-type matches
 185       // a constraint but the sub-type is more constrained
 186       SpanishProductCategory guitarCategory = new SpanishProductCategory(GenerateUniqueString(Types.ProductCategory.Field._name), GenerateUniqueString(Types.ProductCategory.Field._description), GenerateUniqueString(Types.SpanishProductCategory.Field._spanishDescription));
 187       string manufacturerOne = GenerateUniqueString(Types.Guitar.Field._manufacturer);
 188       Guitar guitar = new Guitar(guitarCategory, manufacturerOne, 3);
 189 
 190       string bodyDescription = GenerateUniqueString(Types.SpanishAcousticGuitar.Field._bodyDescription);
 191       SpanishAcousticGuitar acousticGuitar = new SpanishAcousticGuitar(guitarCategory, manufacturerOne, 3, bodyDescription);
 192 
 193       PersistenceFacade.Persist(guitarCategory, guitar, acousticGuitar);
 194 
 195       Query<Guitar> guitarQuery = new Query<Guitar>();
 196       // Matches two rows in the guitar table
 197       guitarQuery.Where.AddEqualTo(Types.Guitar.Field._manufacturer, manufacturerOne);
 198 
 199       // Only matches one row, therefore only one row should be returned
 200       guitarQuery.Where.AddEqualTo(Types.Guitar.SubClass.AcousticGuitar.Field._bodyDescription, bodyDescription);
 201 
 202       // Constrain the same field on the sub-type. We still should be referring to the same row
 203       guitarQuery.Where.AddEqualTo(Types.Guitar.SubClass.AcousticGuitar.SubClass.SpanishAcousticGuitar.Field._bodyDescription, bodyDescription);
 204 
 205       guitarQuery.AddPrefetch(Types.Guitar.SubClass.AcousticGuitar.SubClass.SpanishAcousticGuitar._productCategory);
 206 
 207       ClearAppDomainAndContextCache();
 208 
 209       // Track how many commands are executed against the database
 210       CommandMonitor commandMonitor = new CommandMonitor();
 211 
 212       List<Guitar> guitars = guitarQuery.FindObjectSet();
 213 
 214       Assert.AreEqual(1, guitars.Count, "Found exactly one guitar that has the constrained body description.");
 215 
 216       // Access the SpanishProductCategory relationship; it will be lazy loaded if it is not already prefetched...
 217       string description = ((SpanishAcousticGuitar)guitars[0]).SpanishProductCategory.Description;
 218 
 219       Assert.AreEqual(1, commandMonitor.NumberOfCommandsExecuted, "Only one query was made to the database. i.e., the SpanishProductCategory relationship was not lazy loaded.");
 220     }
 221 
 222     /// <summary>
 223     /// Tests the case where the prefetched relationships are all null.
 224     /// </summary>
 225     [Test]
 226     public void PrefetchWithMultipleNullValuesInPath()
 227     {
 228       RootObject rootObject = RootObject.Create(LegalValue.VALUE_ONE);
 229       PersistenceFacade.Persist(rootObject);
 230       int primaryKey = (int) rootObject.PrimaryKey[0];
 231       ClearAppDomainAndContextCache();
 232       Query<RootObject> rootObjectQuery = new Query<RootObject>();
 233       rootObjectQuery.Where = Types.RootObject.Field._primaryKey.Value == primaryKey;
 234       // The subtypes, basetype, and basetypereferences relationships should be null.
 235       rootObjectQuery.AddPrefetch(Types.RootObject._subTypes._baseType._baseTypeReferences);
 236       RootObject rootObjectFromDatabase =rootObjectQuery.FindObject();
 237       Assert.IsNotNull(rootObjectFromDatabase, "The instance should be retrieved.");
 238     }
 239   }
 240 }