/// <summary> /// Converts a datatable into a set of instances of baseClass. /// </summary> /// <param name="table">A System.Data.DataTable with raw database data.</param> /// <param name="lazyLoading">Indicates whether to enable the lazyLoading on created instances.</param> /// <param name="baseClass">Defines which type to load.</param> /// <exception cref="System.ArgumentNullException">Occurs when table or baseClass parameters are null.</exception> /// <returns>An array of entities.</returns> internal static EntityBase[] BindRows(DataTable table, SearchOptions searchOptions, string baseAlias) { if (table == null) { throw new ArgumentNullException("table"); } if (searchOptions == null) { throw new ArgumentNullException("searchOptions"); } if (searchOptions.eagerLoading.Count == 0) { //TODO: Check if this is necessary by performance needs. EntityBase[] instances = (EntityBase[])(Array.CreateInstance(searchOptions.baseType, table.Rows.Count)); for (int i = 0; i <= instances.Length - 1; i++) { instances[i] = (EntityBase)(Activator.CreateInstance(searchOptions.baseType)); instances[i].Bind(searchOptions.LazyLoading, baseAlias, null, table.Rows[i]); } return(instances); } else { Type listof = typeof(List <>).MakeGenericType(searchOptions.baseType); IList instances = (IList)Activator.CreateInstance(listof); //lets get metadata for baseClass and other things requested by the user. List <FieldInfo[]> meta = new List <FieldInfo[]>(); meta.Add(EntityBase.GetPrimaryKeys(searchOptions.baseType)); List <ForeignKeyInfo> keys = new List <ForeignKeyInfo>(); //i need this to get fks by index foreach (ForeignKeyInfo fkInfo in searchOptions.eagerLoading.Keys) { keys.Add(fkInfo); meta.Add(EntityBase.GetPrimaryKeys(fkInfo.ElementType)); //pks of each eagerloading } List <string>[] lastPk = new List <string> [meta.Count]; for (int i = 0; i < meta.Count; i++) { lastPk[i] = new List <string>(); } foreach (DataRow dr in table.Rows) { string basePk = string.Empty; foreach (FieldInfo fi in meta[0]) { string alias = fi.DataFieldName; basePk += "||" + dr[alias].ToString(); } for (int i = 0; i < meta.Count; i++) { ForeignKeyInfo key = (i == 0 ? null : keys[i - 1]); string currentPKs = basePk; if (i > 0) { foreach (FieldInfo fi in meta[i]) { string alias = (i == 0 ? fi.DataFieldName : searchOptions.eagerLoading[key] + fi.DataFieldName); currentPKs += "||" + dr[alias].ToString(); } } if (!string.IsNullOrEmpty(currentPKs) && !lastPk[i].Contains(currentPKs)) { lastPk[i].Add(currentPKs); EntityBase instance = (EntityBase)Activator.CreateInstance((i == 0 ? searchOptions.baseType : key.ElementType)); instance.Bind(searchOptions.LazyLoading, (i == 0 ? null : searchOptions.eagerLoading[key]), null, dr); if (key == null) { instances.Add(instance); } else { int index = lastPk[0].IndexOf(basePk); EntityBase baseInstance = (EntityBase)instances[index]; if (key.IsArray) { baseInstance.lazyEnabled = false; IList value = (IList)baseInstance.GetPropertyValue(key.RelatedProperty.Name, false); baseInstance.lazyEnabled = true; value.Add(instance); } else { baseInstance.SetPropertyValue(key.RelatedProperty.Name, instance); } } } } } return((EntityBase[])instances.GetType().GetMethod("ToArray").Invoke(instances, null)); } }
/// <summary> /// Loads a foreign key property. /// </summary> /// <param name="property">The property.</param> /// <param name="connection">The connection.</param> /// <remarks></remarks> internal object LoadForeign(System.Reflection.PropertyInfo property, ConnectionStringSettings connection) { if (!propertyData.ContainsKey(property.Name)) { ForeignKeyInfo field = ForeignKeyInfo.Create(property); if (field == null) { throw new Tenor.Data.MissingFieldException(property.DeclaringType, property.Name); } /* * Dim filters As String = "" * Dim params As New List(Of Data.Parameter) */ TableInfo table = TableInfo.CreateTableInfo(field.ElementType); if (connection == null) { connection = table.GetConnection(); } if (!field.IsArray && table.Cacheable) { //We found a cacheble instance, so we don't need to search. EntityBase instance = (EntityBase)Activator.CreateInstance(table.RelatedTable); for (int i = 0; i <= field.ForeignFields.Length - 1; i++) { field.ForeignFields[i].SetPropertyValue(instance, field.LocalFields[i].PropertyValue(this)); } instance.Bind(); field.SetPropertyValue(this, instance); return(instance); } EntityBase[] instances; if (lazyEnabled) { SearchOptions sc = new SearchOptions(field.ElementType); bool fkHasValue = false; if (field.IsManyToMany) { Join j = new Join(GeneralDialect.ManyToManyAlias); j.ForeignKey = field; j.LocalTableInfo = table; sc.Conditions.includes.Add(j); for (int i = 0; i <= field.ForeignFields.Length - 1; i++) { if (i > 0) { sc.Conditions.Add(Tenor.Data.LogicalOperator.And); } SearchConditionForManyToMany scmm = new SearchConditionForManyToMany( GeneralDialect.ManyToManyAlias, field.LocalManyToManyFields[i], field.LocalFields[i].PropertyValue(this)); sc.Conditions.Add(scmm); fkHasValue = true; } } else { //lets find objects, one-to-many and many-to-one //for each Foreign, join an AND operator to match foreign with local value. if (field.ForeignFields.Length != field.LocalFields.Length) { throw new MissingForeignKeyException(this.GetType(), property.Name); } for (int i = 0; i <= field.ForeignFields.Length - 1; i++) { if (i > 0) { sc.Conditions.Add(Tenor.Data.LogicalOperator.And); } object value = field.LocalFields[i].PropertyValue(this); // only one fk field needs to have a value set fkHasValue = fkHasValue || (value != null && value != DBNull.Value); sc.Conditions.Add( /* the foreign property name */ field.ForeignFields[i].RelatedProperty.Name, /* the local value */ value); } if (sc.Conditions.Count == 0) { //this should never happen. throw (new TenorException()); } } // lazy is enabled, go database, go! // only searches if fk field is not null if (fkHasValue) { instances = sc.Execute(connection); } else { instances = new EntityBase[] { } }; } else { //lazy is disabled, so, no data will be retrieved. instances = new EntityBase[] { }; } if (field.IsArray) { if (field.RelatedProperty.PropertyType.IsArray) { propertyData.Add(property.Name, instances); } else { // There must be another way to create it, string is not cool. Type listof = Type.GetType("Tenor.Data.EntityList`1[[" + field.ElementType.AssemblyQualifiedName + "]]"); System.Reflection.ConstructorInfo ctor = listof.GetConstructor(System.Reflection.BindingFlags.CreateInstance | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic, null, new Type[] { typeof(EntityBase), typeof(string) }, null); IList obj = (IList)ctor.Invoke(new object[] { (EntityBase)this, property.Name }); obj.Clear(); foreach (EntityBase i in instances) { obj.Add(i); } propertyData.Add(property.Name, obj); } } else { if (instances.Length == 0) { propertyData.Add(property.Name, null); } else { if (instances.Length > 1) { throw new ManyRecordsFoundException();//"LoadingForeignKey-ManyToOne: More than one instance was returned"); } propertyData.Add(property.Name, instances[0]); } } } return(propertyData[property.Name]); }