1 using System; 2 using System.Collections.Generic; 3 using System.Data; 4 using DomainObjects.DbAccess.Command.SqlStatement.ObjectModel.From; 5 using DomainObjects.Facade.Command; 6 using DomainObjects.Facade.Command.Expression; 7 using DomainObjects.Facade.Test; 8 using DomainObjects.Test.Domain; 9 using NUnit.Framework; 10 11 namespace DomainObjects.Test.TestFixture 12 { 13 /// <summary> 14 /// Tests the generation and behavior of the field reference paths 15 /// generated by the DomainObjectGen.exe tool. 16 /// </summary> 17 [TestFixture] 18 public class GeneratedReferencePathTests : DomainObjectsTestFixture 19 { 20 /// <summary> 21 /// Validates that a <see cref="Field"/> can be stored and retrieved by key. 22 /// </summary> 23 [Test] 24 public void FieldCaching() 25 { 26 // Create a field that will be cached 27 Field fieldToCache = Types.Product._productCategory.CrossJoin.BaseType.Field._primaryKey; 28 29 // After the call to GetKey(), the field 30 // should be cached. 31 string fieldKey = fieldToCache.GetKey(); 32 33 // Retrieve the field from the cache 34 Field retrievedField = Field.GetField(fieldKey); 35 36 // Validate that the original field and the 37 // retrieved field are the same field 38 Assert.AreEqual(fieldToCache, retrievedField, "The field that was cached is the same field that was retrieved."); 39 } 40 41 /// <summary> 42 /// Validates that a subclass can be referenced from the root base class 43 /// in a reference path. Note that this will generate a cross join between 44 /// the base class and the sub class and each type will be represented by 45 /// a different table alias. 46 /// </summary> 47 [Test] 48 public void RootOfPathAllowsSubClasses() 49 { 50 Query<Guitar> guitarQuery = new Query<Guitar>(); 51 guitarQuery.Where.AddNotNull(Types.Guitar.Field._primaryKey); 52 // This reference path ends up replacing the Guitar class with the AcousticGuitar sub class 53 guitarQuery.Where.AddGreaterThan(Types.Guitar.SubClass.AcousticGuitar.Field._numberOfStrings, 0); 54 List<Guitar> guitars = guitarQuery.FindObjectSet(); 55 Assert.Greater(guitars.Count, 0, "Found some guitars."); 56 } 57 58 [Test] 59 public void SingleSubClassAtRootOfPathCorrectlyConstrained() 60 { 61 // Create a set of classes where the base-type matches 62 // a constraint but the sub-type is more constrained 63 ProductCategory guitarCategory = new ProductCategory(GenerateUniqueString(Types.ProductCategory.Field._name), GenerateUniqueString(Types.ProductCategory.Field._description)); 64 string manufacturerOne = GenerateUniqueString(Types.Guitar.Field._manufacturer); 65 Guitar guitar = new Guitar(guitarCategory, manufacturerOne, 3); 66 67 string bodyDescription = GenerateUniqueString(Types.AcousticGuitar.Field._bodyDescription); 68 AcousticGuitar acousticGuitar = new AcousticGuitar(guitarCategory, manufacturerOne, 3, bodyDescription); 69 70 PersistenceFacade.Persist(guitarCategory, guitar, acousticGuitar); 71 72 Query<Guitar> guitarQuery = new Query<Guitar>(); 73 // Matches two rows in the guitar table 74 guitarQuery.Where.AddEqualTo(Types.Guitar.Field._manufacturer, manufacturerOne); 75 // Only matches one row, therefore only one row should be returned 76 guitarQuery.Where.AddEqualTo(Types.Guitar.SubClass.AcousticGuitar.Field._bodyDescription, bodyDescription); 77 78 Assert.AreEqual(1, guitarQuery.GetDataSetCount(), "Found exactly one guitar that has the constrained body description."); 79 } 80 81 [Test] 82 public void MultipleSubClassesAtRootOfPathCorrectlyConstrained() 83 { 84 // Create a set of classes where the base-type matches 85 // a constraint but the sub-type is more constrained 86 SpanishProductCategory guitarCategory = new SpanishProductCategory(GenerateUniqueString(Types.ProductCategory.Field._name), GenerateUniqueString(Types.ProductCategory.Field._description), GenerateUniqueString(Types.SpanishProductCategory.Field._spanishDescription)); 87 string manufacturerOne = GenerateUniqueString(Types.Guitar.Field._manufacturer); 88 Guitar guitar = new Guitar(guitarCategory, manufacturerOne, 3); 89 90 string bodyDescription = GenerateUniqueString(Types.SpanishAcousticGuitar.Field._bodyDescription); 91 SpanishAcousticGuitar acousticGuitar = new SpanishAcousticGuitar(guitarCategory, manufacturerOne, 3, bodyDescription); 92 93 PersistenceFacade.Persist(guitarCategory, guitar, acousticGuitar); 94 95 Query<Guitar> guitarQuery = new Query<Guitar>(); 96 // Matches two rows in the guitar table 97 guitarQuery.Where.AddEqualTo(Types.Guitar.Field._manufacturer, manufacturerOne); 98 99 // Only matches one row, therefore only one row should be returned 100 guitarQuery.Where.AddEqualTo(Types.Guitar.SubClass.AcousticGuitar.Field._bodyDescription, bodyDescription); 101 102 // Constrain the same field on the sub-type. We still should be referring to the same row 103 guitarQuery.Where.AddEqualTo(Types.Guitar.SubClass.AcousticGuitar.SubClass.SpanishAcousticGuitar.Field._bodyDescription, bodyDescription); 104 105 Assert.AreEqual(1, guitarQuery.GetDataSetCount(), "Found exactly one guitar that has the constrained body description."); 106 } 107 108 /// <summary> 109 /// Validates that when a base class left outer joins 110 /// its sub class, that a subclass type constraint is not applied 111 /// to the query. 112 /// </summary> 113 [Test] 114 public void LeftOuterJoinSubClass() 115 { 116 // Create an instance of a 'ProductCategory' that 117 // is related to an instance of 'Guitar' 118 ProductCategory productCategory = new ProductCategory("Nylon String Guitars", "Acoustic Guitars that have nylon strings"); 119 Guitar guitar = new Guitar(productCategory, "McPherson", 6); 120 121 // Persist the new category and guitar 122 PersistenceFacade.Persist(productCategory, guitar); 123 124 // Now find the 6 string guitars associated with the new 125 // product category we created above. (There should be only one) 126 Query<Guitar> query = new Query<Guitar>(); 127 query.Where.AddEqualTo(Types.Guitar.LeftOuterJoinSubClass.AcousticGuitar.Field._numberOfStrings, 6); 128 query.Where.AddEqualTo(Types.Guitar.LeftOuterJoinSubClass.AcousticGuitar.Field._productCategoryId, productCategory.PrimaryKey[0]); 129 130 // Retrieve the number of strings 131 query.AddSelectField(Types.Guitar.LeftOuterJoinSubClass.AcousticGuitar.Field._numberOfStrings.As("NumberOfStrings")); 132 133 /****** Here's where we are Left Outer Joining the Sub Class **********/ 134 135 // Always retrieve a body description whether or not the 136 // row is an instance of 'AcousticGuitar' 137 query.AddSelectField(Types.Guitar.LeftOuterJoinSubClass.AcousticGuitar.Field._bodyDescription.As("BodyDescription")); 138 139 // Execute the query 140 DataSet guitarDataSet = query.FindDataSet(); 141 142 // Validate that we found the instance via the query 143 Assert.AreEqual(1, guitarDataSet.Tables[0].Rows.Count, "Found exactly one row."); 144 Assert.AreEqual(6, guitarDataSet.Tables[0].Rows[0]["NumberOfStrings"], "The number of strings is 6."); 145 Assert.AreEqual(DBNull.Value, guitarDataSet.Tables[0].Rows[0]["BodyDescription"], "The body description is null."); 146 147 // From the guitarDataSet query, DomainObjects generates the following SQL SELECT statement. 148 // Note that there are no concrete type constraints: 149 // 150 // SELECT GUITARS12.NumberOfStrings AS NumberOfStrings, 151 // GUITARS12.BodyDesc AS BodyDescription 152 // FROM GUITARS AS GUITARS12 153 // WHERE ((GUITARS12.NumberOfStrings = @GUITARS12_NumberOfStrings_0 154 // AND GUITARS12.PRODUCT_CATEGORY_ID = @GUITARS12_PRODUCT_CATEGORY_ID_1)) 155 // 156 // Parameter values: 157 // @GUITARS12_NumberOfStrings_0 = 6 158 // @GUITARS12_PRODUCT_CATEGORY_ID_1 = 64 159 160 161 // Now let's query for the Guitar instance without left outer joining 162 // i.e., by inner joining the subclass. This will cause DomainObjects to 163 // add a concrete type constraint to the WHERE clause which ensures that 164 // only instances of AcousticGuitar or its subclasses are retrieved. 165 Query<Guitar> query2 = new Query<Guitar>(); 166 query2.Where.AddEqualTo(Types.Guitar.SubClass.AcousticGuitar.Field._numberOfStrings, 6); 167 query2.Where.AddEqualTo(Types.Guitar.SubClass.AcousticGuitar.Field._productCategoryId, productCategory.PrimaryKey[0]); 168 169 // Retrieve the number of strings 170 query2.AddSelectField(Types.Guitar.SubClass.AcousticGuitar.Field._numberOfStrings.As("NumberOfStrings")); 171 172 // retrieve a body description if the 173 // row is an instance of 'AcousticGuitar' 174 query2.AddSelectField(Types.Guitar.SubClass.AcousticGuitar.Field._bodyDescription.As("BodyDescription")); 175 176 // Execute the query 177 DataSet acousticGuitarDataSet = query2.FindDataSet(); 178 179 // Validate that we did not find any instances of AcousticGuitar or its subclasses 180 Assert.AreEqual(0, acousticGuitarDataSet.Tables[0].Rows.Count, "Did not find any instances of AcousticGuitar or its subclasses."); 181 182 // From the acousticGuitarDataSet query, DomainObjects generates the following SQL SELECT statement. 183 // Note that concrete type constraints have been added to the statement: 184 // 185 // SELECT GUITARS12.NumberOfStrings AS NumberOfStrings, 186 // GUITARS12.BodyDesc AS BodyDescription 187 // FROM GUITARS AS GUITARS12 188 // WHERE ((GUITARS12.NumberOfStrings = @GUITARS12_NumberOfStrings_0 189 // AND GUITARS12.PRODUCT_CATEGORY_ID = @GUITARS12_PRODUCT_CATEGORY_ID_1) 190 // AND ((GUITARS12.ConcreteClass IN ( @GUITARS12_ConcreteClass_2, @GUITARS12_ConcreteClass_3)))) 191 // 192 // Parameter values: 193 // @GUITARS12_NumberOfStrings_0 = 6 194 // @GUITARS12_PRODUCT_CATEGORY_ID_1 = 65 195 // @GUITARS12_ConcreteClass_2 = 'DomainObjects.Test.Domain.AcousticGuitar' 196 // @GUITARS12_ConcreteClass_3 = 'DomainObjects.Test.Domain.SpanishAcousticGuitar' 197 } 198 199 /// <summary> 200 /// Validates that when a base class left outer joins 201 /// its sub class, that a subclass type constraint is not applied 202 /// to the query. 203 /// </summary> 204 [Test] 205 public void LeftOuterJoinSubClass2() 206 { 207 // Create an instance of a 'ProductCategory' that 208 // is related to an instance of 'Guitar' 209 SpanishProductCategory productCategory = new SpanishProductCategory(GenerateUniqueString(Types.SpanishProductCategory.Field._name), GenerateUniqueString(Types.SpanishProductCategory.Field._description), GenerateUniqueString(Types.SpanishProductCategory.Field._spanishDescription)); 210 string manufacturer = GenerateUniqueString(Types.SpanishAcousticGuitar.Field._manufacturer); 211 string bodyDescription = GenerateUniqueString(Types.SpanishAcousticGuitar.Field._bodyDescription); 212 SpanishAcousticGuitar guitar = new SpanishAcousticGuitar(productCategory, manufacturer, 8, bodyDescription); 213 214 // Persist the new category and guitar 215 PersistenceFacade.Persist(productCategory, guitar); 216 217 // Now find the 6 string guitars associated with the new 218 // product category we created above. (There should be only one) 219 Query<Guitar> query = new Query<Guitar>(); 220 query.Where.AddEqualTo(Types.Guitar.Field._manufacturer, manufacturer); 221 query.Where.AddEqualTo(Types.Guitar.LeftOuterJoinSubClass.AcousticGuitar.SubClass.SpanishAcousticGuitar.Field._productCategoryId, productCategory.PrimaryKey[0]); 222 223 // Retrieve the number of strings 224 query.AddSelectField(Types.Guitar.LeftOuterJoinSubClass.AcousticGuitar.Field._numberOfStrings.As("NumberOfStrings")); 225 226 /****** Here's where we are Left Outer Joining the Sub Class **********/ 227 228 // Always retrieve a body description whether or not the 229 // row is an instance of 'AcousticGuitar' 230 query.AddSelectField(Types.Guitar.LeftOuterJoinSubClass.AcousticGuitar.LeftOuterJoinSubClass.SpanishAcousticGuitar.Field._bodyDescription.As("BodyDescription")); 231 232 // Execute the query 233 DataSet guitarDataSet = query.FindDataSet(); 234 235 // Validate that we found the instance via the query 236 Assert.AreEqual(1, guitarDataSet.Tables[0].Rows.Count, "Found exactly one row."); 237 Assert.AreEqual(8, guitarDataSet.Tables[0].Rows[0]["NumberOfStrings"], "The correct number of strings was retrieved."); 238 Assert.AreEqual(bodyDescription, guitarDataSet.Tables[0].Rows[0]["BodyDescription"], "The body description was retrieved."); 239 } 240 241 [Test] 242 public void ChangeReferenceTo() 243 { 244 ProductQuery pathToProduct = Types.Product; 245 246 ColumnField primaryKey = pathToProduct._productCategory.Field._primaryKey; 247 248 // The only check at this point is that an exception is not thrown 249 ColumnField name = pathToProduct._productCategory.Field._name; 250 } 251 252 [Test] 253 public void ChangeReferenceToAndSelectField() 254 { 255 ProductCategoryQuery pathToProductCategory = Types.Product._productCategory; 256 257 ColumnField primaryKey = pathToProductCategory._guitarsInThisCategory.Field._primaryKey; 258 259 // The only check at this point is that an exception is not thrown 260 ColumnField unit = pathToProductCategory._productsInThisCategory.Field._unit; 261 262 Query<Product> productQuery = new Query<Product>(); 263 productQuery.AddSelectField(primaryKey); 264 productQuery.AddSelectField(unit); 265 266 productQuery.FindDataSet(); 267 } 268 } 269 }