/// <summary> /// Creates the SQL query based on conditions using the current dialect. /// Generally, you can call this method for debugging reasons. /// </summary> /// <param name="searchOptions">The search definitions.</param> /// <param name="connection">The Connection.</param> /// <param name="justCount">Indicates that this is a COUNT query.</param> /// <param name="skip">Number of rows to skip when paging</param> /// <param name="take">Number of rows to take when paging</param> /// <param name="parameters">Outputs the generated parameters.</param> /// <returns>A SQL query.</returns> public static string GetSearchSql(SearchOptions searchOptions, bool justCount, int?skip, int?take, ConnectionStringSettings connection, out TenorParameter[] parameters) { GeneralDialect dialect = DialectFactory.CreateDialect(connection); if (searchOptions == null) { throw (new ArgumentNullException("searchOptions", "You must specify a SearchOptions instance.")); } TableInfo table = TableInfo.CreateTableInfo(searchOptions.baseType); if (connection == null) { connection = table.GetConnection(); } //Read Projections List <Projection> projections = new List <Projection>(); foreach (Projection p in searchOptions.Projections) { projections.Add(p); } //Read Joins List <Join> joins = new List <Join>(); joins.AddRange(GetPlainJoins(searchOptions, dialect)); //Get necessary fields to create the select statement. StringBuilder sqlFields = new StringBuilder(); List <FieldInfo> fieldInfos = new List <FieldInfo>(); List <SpecialFieldInfo> spFields = new List <SpecialFieldInfo>(); List <FieldInfo> lenFields = new List <FieldInfo>(); FieldInfo[] allFields; if (justCount) { allFields = EntityBase.GetFields(searchOptions.baseType, true); //lets count using distinct subquery } else if (searchOptions.Projections.Count > 0) { allFields = ReadProjections <FieldInfo>(projections, null, EntityBase.GetFields(searchOptions.baseType)); } else { allFields = EntityBase.GetFields(searchOptions.baseType); //lets get all fields } foreach (FieldInfo f in allFields) { if (f.PrimaryKey || (!f.LazyLoading && !justCount)) //primary keys and eager fields must be loaded { fieldInfos.Add(f); } else if (!justCount && f.LazyLoading && f.FieldType.IsAssignableFrom(typeof(BinaryStream))) { lenFields.Add(f); } // when paging, at least one sorting criterion is needed if (skip.HasValue && take.HasValue && searchOptions.Sorting.Count == 0 && f.PrimaryKey) { searchOptions.Sorting.Add(f.RelatedProperty.Name); } } if (!justCount) //we don't need it on counting { SpecialFieldInfo[] fields = EntityBase.GetSpecialFields(searchOptions.baseType); if (searchOptions.Projections.Count > 0) { fields = ReadProjections <SpecialFieldInfo>(projections, null, fields); } spFields.AddRange(fields); } sqlFields.Append(dialect.CreateSelectSql(table.RelatedTable, null, fieldInfos.ToArray(), spFields.ToArray(), lenFields.ToArray())); //adding values from eager loading types foreach (ForeignKeyInfo fkInfo in searchOptions.eagerLoading.Keys) { fieldInfos = new List <FieldInfo>(); spFields = new List <SpecialFieldInfo>(); lenFields = new List <FieldInfo>(); //select all fields, or only the projection. FieldInfo[] allEagerFields = EntityBase.GetFields(fkInfo.ElementType); if (searchOptions.Projections.Count > 0) { allEagerFields = ReadProjections <FieldInfo>(projections, fkInfo.RelatedProperty.Name, allEagerFields); } foreach (FieldInfo f in allEagerFields) { if (f.PrimaryKey || !f.LazyLoading) { fieldInfos.Add(f); } else if (f.LazyLoading && f.FieldType.IsAssignableFrom(typeof(BinaryStream))) { lenFields.Add(f); } } //spfields SpecialFieldInfo[] allSpFields = EntityBase.GetSpecialFields(fkInfo.ElementType); if (searchOptions.Projections.Count > 0) { allSpFields = ReadProjections <SpecialFieldInfo>(projections, fkInfo.RelatedProperty.Name, allSpFields); } spFields.AddRange(allSpFields); //joins: joins was made on GetPlainJoins. sqlFields.Append(", "); sqlFields.Append(dialect.CreateSelectSql(fkInfo.ElementType, searchOptions.eagerLoading[fkInfo], fieldInfos.ToArray(), spFields.ToArray(), lenFields.ToArray())); } //Sorting (not necessary for count queries) string sqlSort = string.Empty; if (!justCount) { string appendToSelect = null; sqlSort = dialect.CreateSortSql(searchOptions.Sorting, table.RelatedTable, joins.ToArray(), searchOptions.Distinct, skip.HasValue && take.HasValue, out appendToSelect); if (!string.IsNullOrEmpty(appendToSelect)) { sqlFields.Append(appendToSelect); } } //Check if we found all projections: if (projections.Count > 0) { throw new InvalidProjectionException(projections[0]); } //Creates the where part string sqlWhere = dialect.CreateWhereSql(searchOptions.Conditions, searchOptions.baseType, joins.ToArray(), out parameters); // Creates the join parts string sqlJoins = dialect.CreateJoinsSql(joins.ToArray()); // Creates the entire sql string string sql = dialect.CreateFullSql(searchOptions.baseType, searchOptions.Distinct, justCount, searchOptions.Top, skip, take, sqlFields.ToString(), sqlJoins, sqlSort, sqlWhere); Tenor.Diagnostics.Debug.DebugSQL("GetSearchSql()", sql, parameters, connection); #if DEBUG LastSearches.Push(sql); #endif return(sql); }
/// <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]); } }