internal static ForeignKeyInfo Create(System.Reflection.PropertyInfo theProperty) { if (theProperty == null) { return(null); } ForeignKeyInfo fk = new ForeignKeyInfo(); fk._RelatedProperty = theProperty; fk.fieldAttributes = (ForeignKeyFieldAttribute[])(fk.RelatedProperty.GetCustomAttributes(typeof(ForeignKeyFieldAttribute), true)); ForeignKeyAttribute[] fkAttribs = (ForeignKeyAttribute[])(fk.RelatedProperty.GetCustomAttributes(typeof(ForeignKeyAttribute), true)); if (fk.fieldAttributes.Length == 0) { return(null); } else { if (fkAttribs.Length == 0) { fk.foreignKeyDefinition = new ForeignKeyAttribute(); } else { fk.foreignKeyDefinition = fkAttribs[0]; } return(fk); } }
/// <summary> /// Sets the LocalTableInfo and ForeignTableInfo of all joins recursivelly. /// </summary> /// <param name="list"></param> /// <param name="join"></param> /// <param name="baseClass"></param> private static void SetProperty(List <Join> list, Join join, Type baseClass) { if (join.LocalTableInfo == null) { if (string.IsNullOrEmpty(join.ParentAlias)) { join.LocalTableInfo = TableInfo.CreateTableInfo(baseClass); } else { int pos = list.IndexOf(new Join(join.ParentAlias)); if (pos == -1) { throw new InvalidOperationException("Cannot find the parent alias for '" + join.JoinAlias + "'"); } Join parent = list[pos]; if (parent.LocalTableInfo == null) { SetProperty(list, parent, baseClass); } join.LocalTableInfo = TableInfo.CreateTableInfo(parent.ForeignKey.ElementType); } } join.ForeignKey = ForeignKeyInfo.Create(join.LocalTableInfo.RelatedTable.GetProperty(join.PropertyName)); if (join.ForeignKey == null) { throw new InvalidOperationException("Cannot find '" + join.PropertyName + "' on '" + join.LocalTableInfo.RelatedTable.Name + "' class. You must define a ForeignKey on that class."); } join.ForeignTableInfo = TableInfo.CreateTableInfo(join.ForeignKey.ElementType); }
/// <summary> /// </summary> internal static ForeignKeyInfo[] GetForeignKeys(Type instanceType) { List <ForeignKeyInfo> res = new List <ForeignKeyInfo>(); foreach (System.Reflection.PropertyInfo i in instanceType.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic)) { ForeignKeyInfo foreign = ForeignKeyInfo.Create(i); if (foreign != null) { res.Add(foreign); } } return(res.ToArray()); }
/// <summary> /// Includes the property on the eager loading list. /// </summary> /// <param name="foreignPropertyName">The name of the property on the base class of this SearchOptions.</param> public void LoadAlso(string foreignPropertyName) { if (string.IsNullOrEmpty(foreignPropertyName)) { throw new ArgumentNullException("foreignPropertyName"); } ForeignKeyInfo fkInfo = ForeignKeyInfo.Create(baseType.GetProperty(foreignPropertyName)); if (fkInfo == null) { throw new MissingForeignKeyException(baseType, foreignPropertyName); } //TODO: Find a better alias name. string alias = foreignPropertyName; eagerLoading.Add(fkInfo, alias); }
protected void Init(EntityBase Parent, string RelatedPropertyName, Type RelatedPropertyType) { _Parent = Parent; System.Reflection.PropertyInfo prop; if (RelatedPropertyType != null) { prop = Parent.GetType().GetProperty(RelatedPropertyName, RelatedPropertyType); } else { prop = Parent.GetType().GetProperty(RelatedPropertyName); } ForeignKey = ForeignKeyInfo.Create(prop); if (ForeignKey == null) { throw new MissingForeignKeyException(prop.DeclaringType, prop.Name); } list = new List <T>(); }
/// <summary> /// Persists a list on the database. /// </summary> /// <param name="propertyName">The name of a many-to-many property on this class.</param> public virtual void SaveList(string propertyName) { if (propertyName == null) { throw new ArgumentNullException("propertyName"); } System.Reflection.PropertyInfo prop = this.GetType().GetProperty(propertyName); if (prop == null) { throw new ArgumentException(string.Format("The property '{0}' was not found on '{1}'.", propertyName, this.GetType().FullName), "propertyName"); } ForeignKeyInfo fkInfo = ForeignKeyInfo.Create(prop); if (fkInfo == null) { throw new InvalidMappingException(this.GetType()); } if (!fkInfo.IsManyToMany) { throw new TenorException("Currently, only many-to-many relations are supported"); } TableInfo table = TableInfo.CreateTableInfo(this.GetType()); if (table == null) { throw new InvalidMappingException(this.GetType()); } ConnectionStringSettings connection = (tenorTransaction == null ? table.GetConnection() : tenorTransaction.Connection); GeneralDialect dialect = DialectFactory.CreateDialect(connection); TenorParameter[] parameters; DbTransaction t = (tenorTransaction == null ? null : tenorTransaction.dbTransaction); //if (dialect.GetType() == typeof(Tenor.Data.Dialects.TSql.TSql)) //{ // //oh god! do you have a better idea on where to write this code? // System.Data.SqlClient.SqlBulkCopy bulk; // if (t == null) // bulk = new System.Data.SqlClient.SqlBulkCopy(tenorTransaction.Connection.ConnectionString); // else // bulk = new System.Data.SqlClient.SqlBulkCopy((System.Data.SqlClient.SqlConnection)t.Connection, System.Data.SqlClient.SqlBulkCopyOptions.Default, (System.Data.SqlClient.SqlTransaction)t); // bulk.DestinationTableName = dialect.GetPrefixAndTable(fkInfo.ManyToManyTablePrefix, fkInfo.ManyToManyTable); // System.Data.DataTable data; // string sql = dialect.CreateSaveList(table, fkInfo, this, out parameters, out data); // foreach (DataColumn col in data.Columns) // { // bulk.ColumnMappings.Add(col.ColumnName, col.ColumnName); // } // Helper.ExecuteQuery(sql, parameters, t, dialect); // bulk.WriteToServer(data); // bulk.Close(); //} //else //{ string sql = dialect.CreateSaveListSql(table, fkInfo, this, out parameters); Helper.ExecuteQuery(sql, parameters, t, dialect); //} }
/// <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]); }
/// <summary> /// Gets the value of a lazy tagged property. /// This call can do a database access. /// </summary> internal virtual object GetPropertyValue(string propertyName, bool forceGetBinary) { //only loads if not loaded yet. //TODO: provide a way to reload from database. lock (propertyData) { if (forceGetBinary || !propertyData.ContainsKey(propertyName)) { //Getting class metadata. TableInfo table = TableInfo.CreateTableInfo(this.GetType()); System.Reflection.PropertyInfo fieldP = this.GetType().GetProperty(propertyName); if (fieldP == null) { throw new Tenor.Data.MissingFieldException(this.GetType(), propertyName); } ForeignKeyInfo fkInfo = null; fkInfo = ForeignKeyInfo.Create(fieldP); if (fkInfo != null) { // this is an FK, so, route to fk loading return(LoadForeign(fieldP, null)); } else { //Continue to the lazy property (lazy fields like byte[]) loading FieldInfo field = FieldInfo.Create(fieldP); if (field == null) { throw new Tenor.Data.MissingFieldException(this.GetType(), fieldP.Name); } if (!forceGetBinary && (field.FieldType == typeof(BinaryStream) || field.FieldType == typeof(BinaryStream).BaseType)) { propertyData[propertyName] = new BinaryStream(this, propertyName); } else { GeneralDialect dialect = DialectFactory.CreateDialect(table.GetConnection()); ConditionCollection conditions = new ConditionCollection(); foreach (FieldInfo f in GetFields(this.GetType(), true)) { conditions.Add(f.RelatedProperty.Name, f.PropertyValue(this)); } if (conditions.Count == 0) { throw (new Tenor.Data.MissingPrimaryKeyException(this.GetType())); } TenorParameter[] parameters = null; string fieldsPart = dialect.CreateSelectSql(table.RelatedTable, null, new FieldInfo[] { field }, null, null); string wherePart = dialect.CreateWhereSql(conditions, table.RelatedTable, null, out parameters); string sql = dialect.CreateFullSql(table.RelatedTable, false, false, 0, fieldsPart, null, null, wherePart); Tenor.Diagnostics.Debug.DebugSQL("GetPropertyValue()", sql, parameters, table.GetConnection()); #if DEBUG LastSearches.Push(sql); #endif Tenor.Data.DataTable rs = new Tenor.Data.DataTable(sql, parameters, table.GetConnection()); rs.Bind(); if (rs.Rows.Count == 0) { throw (new RecordNotFoundException()); } else if (rs.Rows.Count > 1) { throw new ManyRecordsFoundException(); } else { var obj = rs.Rows[0][field.DataFieldName]; if (obj == DBNull.Value) { obj = null; } if (forceGetBinary) { if (obj == null) { return new byte[] { } } ; else { return(obj); } } else { propertyData[propertyName] = obj; } } } } } return(propertyData[propertyName]); } }