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 }