public ValidationMap( ObjectMap map, string propertyName, Type propertyType, ValidatorBaseAttribute va ) { this.map = map; this.propertyName = propertyName; this.propertyType = propertyType; this.va = va; }
public ObjectConstructor( ObjectMap objectMap ) { this.objectMap = objectMap; constructorInfos = Reflector.FindConstructors( objectMap.Type ); constructorMaps = new Hashtable(); columnInfos = new Hashtable(); }
public ConstructorMap( ObjectMap objectMap, ConstructorInfo constructorInfo, ColumnInfo columnInfo, object[] row ) { this.objectMap = objectMap; this.constructorInfo = constructorInfo; this.columnInfo = columnInfo; InitBitArrays( Math.Max( objectMap.Fields.Count, row.Length ) ); InitConstructorMap( row ); }
protected virtual void InitParent( IPersistent parent ) { Check.VerifyNotNull( parent, Error.NullParameter, "parent" ); Check.Verify( parent.IsPersisted || ! parentMap.IsAutoGeneratedPrimaryKey, Error.DeveloperError, "The parent object must have been persisted before you can use the list." ); this.parent = parent; parentMap = ObjectFactory.GetMap( broker, parent ); }
protected virtual void InitType( Type containedType, IPersistent parent, Type viaType ) { containedMap = ObjectFactory.GetMap( broker, containedType ); Check.Verify( containedType.GetInterface( "IPersistent", false ) != null, Error.UnsupportedType, containedType ); listType = parent == null ? GentleListType.StandAlone : (viaType == null ? GentleListType.OneToMany : GentleListType.ManyToMany); }
public ViewMap( ObjectMap map, string propertyName, Type propertyType, CustomViewAttribute cv ) { this.map = map; this.propertyName = propertyName; this.propertyType = propertyType; this.cv = cv; if( map != null && cv.NavigateUrlFormat != null ) { string[] keys = map.GetPrimaryKeyNames( true ); primaryKeyName = keys[ 0 ]; } }
/// <summary> /// Create a new list for storing the specified type of objects. /// </summary> public GentleRelation( PersistenceBroker broker, Type containedType, IPersistent parent, params Type[] relatedTypes ) : base( broker ) { Check.VerifyNotNull( relatedTypes, Error.NullParameter, "relatedTypes" ); /*Check.Verify( parent == null && relatedTypes.Length == 2 || parent != null && relatedTypes.Length == 1, Error.NotImplemented, "The GentleRelation class is currently only able to manage two-way relations." ); */ this.relatedTypes = relatedTypes; this.parent = parent; containedMap = ObjectFactory.GetMap( broker, containedType ); // note: "this." required on broker param or original null param might get passed relations = new GentleList( this.broker, containedType, parent ); // verify that the linked types are supported by Gentle foreach( Type relatedType in relatedTypes ) { Check.Verify( relatedType.IsSubclassOf( typeof(Persistent) ) || relatedType.GetInterface( "IPersistent" ) != null, Error.UnsupportedType, relatedType ); } }
/// <summary> /// Constructor for fields using information obtained from the TableColumn attribute. /// </summary> public FieldMap( ObjectMap map, MemberAttributeInfo memberAttributeInfo ) { this.map = map; memberInfo = memberAttributeInfo.MemberInfo; if( memberInfo is PropertyInfo ) { memberType = (memberInfo as PropertyInfo).PropertyType; } else { memberType = (memberInfo as FieldInfo).FieldType; } // extract information from attributes if( memberAttributeInfo.Attributes != null ) { foreach( Attribute attr in memberAttributeInfo.Attributes ) { if( attr is TableColumnAttribute ) { TableColumnAttribute tc = attr as TableColumnAttribute; SetColumnName( tc.Name ?? memberInfo.Name ); SetNullValue( tc.NullValue ); SetIsNullable( ! tc.NotNull ); SetSize( tc.Size ); if( tc.HasDbType ) { SetDbType( tc.DatabaseType ); } else { SetDbType( NO_DBTYPE ); } handleEnumAsString = tc.HandleEnumAsString; isReadOnly = tc.IsReadOnly; isUpdateAfterWrite = tc.IsUpdateAfterWrite; } else if( attr is PrimaryKeyAttribute ) { PrimaryKeyAttribute pk = attr as PrimaryKeyAttribute; SetIsPrimaryKey( true ); SetIsAutoGenerated( pk.AutoGenerated ); if( IsAutoGenerated ) { map.IdentityMap = this; } } else if( attr is ForeignKeyAttribute ) { ForeignKeyAttribute fk = attr as ForeignKeyAttribute; SetForeignKeyTableName( fk.ForeignTable ); SetForeignKeyColumnName( fk.ForeignColumn ); } else if( attr is ConcurrencyAttribute ) { ConcurrencyAttribute ca = attr as ConcurrencyAttribute; Check.Verify( memberType == typeof(int) || memberType == typeof(long), Error.DeveloperError, "Unsupported property type {0} for ConcurrencyAttribute {1}", memberInfo.ReflectedType, memberInfo.Name ); isConcurrencyColumn = true; map.ConcurrencyMap = this; } else if( attr is SoftDeleteAttribute ) { SoftDeleteAttribute sd = attr as SoftDeleteAttribute; Check.Verify( memberType == typeof(int) || memberType == typeof(long), Error.DeveloperError, "Unsupported property type {0} for SoftDeleteAttribute {1}", memberInfo.ReflectedType, memberInfo.Name ); isSoftDeleteColumn = true; map.SoftDeleteMap = this; } else if( attr is SequenceNameAttribute ) { // sequence name used when available (in place of name conventions or autodetected value) sequenceName = (attr as SequenceNameAttribute).Name; } else if( attr is InheritanceAttribute ) { // sequence name used when available (in place of name conventions or autodetected value) map.InheritanceMap = this; } } } }
/// <summary> /// This method assumes that the tableName and identityColumn parameters are passed /// in the correct case. /// Please refer to the documentation of <see cref="GentleSqlFactory"/> for details on the /// purpose of this method. /// </summary> /// <param name="sql">The sql string to which we should append</param> /// <param name="om">An <see cref="Gentle.Framework.ObjectMap"/> instance of the object for which to retrieve the identity select</param> /// <returns>The modified sql string which also retrieves the identity value</returns> public override string GetIdentitySelect( string sql, ObjectMap om ) { if( provider.ProviderInformation.Version.Major >= 2 ) { // strip trailing whitespace and statement terminator sql = sql.TrimEnd( ' ', ';' ); string name = om.IdentityMap.ColumnName; if( IsReservedWord( name ) ) { name = QuoteReservedWord( name ); } return String.Format( "{0} returns {1}", sql, name ); } else // try to obtain value using sequence name { // If the second parameter to GEN_ID is 1 a new value will be generated. // If the value is 0, the current value is retrieved. if( om.IdentityMap.SequenceName != null ) { return String.Format( "select GEN_ID({0}, 0) from rdb$database", om.IdentityMap.SequenceName ); } else { return String.Format( "select GEN_ID(GEN_{0}_{1}, 0) from rdb$database", om.TableName.ToUpper(), om.IdentityMap.ColumnName.ToUpper() ); } } }
internal ObjectView( ObjectMap map, string viewName ) { this.map = map; this.viewName = viewName; viewMaps = new ArrayList(); }
/// <summary> /// Returns a boolean value indicating whether the current key instance contains exactly /// the fields compromising the primary key fields of the given type. This is used to /// determine whether a cached statement can be used (non-primary key select statements /// are not cached). /// </summary> /// <param name="map">The ObjectMap describing a type and its table mapping.</param> /// <param name="ignoreConcurrencyColumn"></param> /// <returns>True if the key contains only and exactly the primary key fields of the /// given type.</returns> public bool IsPrimaryKeyFields( ObjectMap map, bool ignoreConcurrencyColumn ) { // determine expected field count int expectedKeyCount = map.PrimaryKeyCount; if( GentleSettings.ConcurrencyControl && map.ConcurrencyMap != null && ! ignoreConcurrencyColumn && ! map.ConcurrencyMap.IsPrimaryKey ) { expectedKeyCount += 1; } bool result = Count == expectedKeyCount; if( result ) { foreach( FieldMap fm in map.Fields ) { string name = isPropertyKeys ? fm.MemberName : fm.ColumnName; result &= ! fm.IsPrimaryKey || (fm.IsPrimaryKey && ContainsKey( name )); } } return result; }
/// <summary> /// Process the given type and construct the corresponding ObjectMap instance. /// </summary> /// <param name="broker">The PersistenceBroker to use for obtaining metadata on the type. If /// null is passed the DefaultProvider will be used.</param> /// <param name="type">The type to process.</param> /// <returns>An ObjectMap instance describing the type</returns> protected static ObjectMap ConstructMap(PersistenceBroker broker, Type type) { Check.Verify(IsTypeSupported(type), Error.UnsupportedType, type); ObjectMap map = new ObjectMap(broker, type); // get persistence attributes IList memberAttributeInfos = Reflector.FindMembers(Reflector.InstanceCriteria, type, false, typeof(TableColumnAttribute), typeof(PrimaryKeyAttribute), typeof(ForeignKeyAttribute), typeof(ConcurrencyAttribute), typeof(SoftDeleteAttribute), typeof(SequenceNameAttribute), typeof(InheritanceAttribute)); foreach (MemberAttributeInfo mai in memberAttributeInfos) { map.Fields.Add(new FieldMap(map, mai)); } // get custom view(s) memberAttributeInfos = Reflector.FindMembers(Reflector.InstanceCriteria, type, true, typeof(CustomViewAttribute)); foreach (MemberAttributeInfo mai in memberAttributeInfos) { if (mai.MemberInfo is PropertyInfo) { foreach (CustomViewAttribute cv in mai.Attributes) { PropertyInfo pi = mai.MemberInfo as PropertyInfo; // create a viewmap for this attribute ViewMap vm = new ViewMap(map, pi.Name, pi.PropertyType, cv); // add it to the specified view map.AddViewColumn(cv.ViewName, vm); } } } // get validation(s) memberAttributeInfos = Reflector.FindMembers(Reflector.InstanceCriteria, type, false, typeof(ValidatorBaseAttribute)); foreach (MemberAttributeInfo mai in memberAttributeInfos) { foreach (ValidatorBaseAttribute vb in mai.Attributes) { PropertyInfo pi = mai.MemberInfo as PropertyInfo; // create a validation map for this attribute ValidationMap va = new ValidationMap(map, pi.Name, pi.PropertyType, vb); // add it to the specified view map.Validations.Add(va); } } // analyze the actual database schema and update our internal maps accordingly if (GentleSettings.AnalyzerLevel != AnalyzerLevel.None) { if (broker == null) { broker = new PersistenceBroker(type); } IDatabaseAnalyzer da = broker.Provider.GetAnalyzer(); if (da != null) // not all persistence engines may support this { da.UpdateObjectMap(map); } } // return the generated object <-> database map return(map); }
/// <summary> /// Create and populate a DataView from the given SqlResult. Columns in the result set /// are copied verbatim to the DataView. /// </summary> /// <param name="sr">The SqlResult used to generate and populate the DataView.</param> /// <returns>The new DataView instance</returns> public static DataView GetDataView( SqlResult sr ) { DataTable dt = new DataTable(); ObjectMap map = null; if( sr.Statement != null && sr.Statement.Type != null ) { map = ObjectFactory.GetMap( sr.SessionBroker, sr.Statement.Type ); } foreach( string columnName in sr.ColumnNames ) { DataColumn column = new DataColumn( columnName ); Type type = GetColumnType( columnName, map, sr ); if( type != null ) { column.DataType = type; } dt.Columns.Add( column ); } DataRow dr; foreach( object[] row in sr.Rows ) { dr = dt.NewRow(); dr.ItemArray = row; dt.Rows.Add( dr ); } return new DataView( dt ); }
/// <summary> /// Please refer to the documentation of <see cref="GentleSqlFactory"/> for details. /// </summary> public override string GetIdentitySelect( string sql, ObjectMap om ) { if( provider.ProviderInformation.Version.Major >= 8 ) { return string.Format( "{0}{1} select SCOPE_IDENTITY()", sql, GetStatementTerminator() ); } else { return string.Format( "{0}{1} select @@IDENTITY", sql, GetStatementTerminator() ); } }
/// <summary> /// Please refer to the documentation of <see cref="GentleSqlFactory"/> for details. /// </summary> public override string GetIdentitySelect( string sql, ObjectMap om ) { //Jet does not support more than one SQL statement //To be executed at the same time return "SELECT @@IDENTITY"; // Access 97/ Jet 3.x does no support the SELECT @@IDENTITY //return string.Format( "SELECT max ({0}) from {1}", om.IdentityMap.ColumnName, om.TableName ); }
/// <summary> /// Get the statement for retrieving last inserted row id for auto-generated id columns /// </summary> /// <param name="sql"></param> /// <param name="om">An <see cref="Gentle.Framework.ObjectMap"/> instance of the object for which to retrieve the identity select</param> /// <returns></returns> public abstract string GetIdentitySelect( string sql, ObjectMap om );
/// <summary> /// Please refer to the documentation of <see cref="GentleSqlFactory"/> for details. /// </summary> public override string GetIdentitySelect( string sql, ObjectMap om ) { //Jet does not support more than one SQL statement //To be executed at the same time return "SELECT @@IDENTITY"; }
/// <summary> /// <p>Get the statement for retrieving last inserted row id for auto-generated id columns.</p> /// <p>The value of this property varies between different persistence engines.</p> /// </summary> /// <param name="sql"></param> /// <param name="om"></param> /// <returns></returns> public string GetIdentitySelect( string sql, ObjectMap om ) { if( sf.HasCapability( Capability.BatchQuery ) ) { return sf.GetIdentitySelect( sql, om ); } else { //Since the persistence engine does not support Batch Command Processing //Two seperate commands must be executed. //Execute the results of GentleSqlFactory.GetIdentitySelect to get the last inserted row id return sql; } }
/// <summary> /// Construct a new SqlBuilder instance for constructing a statement of the given type /// for the specified business class type. /// </summary> /// <param name="provider">The IGentleProvider used to obtain an SqlFactory</param> /// <param name="stmtType">The type of SQL statement to construct</param> /// <param name="type">The object class to work on</param> /// <param name="logicalOperator">The logic operator used with constraints (can be either AND or OR)</param> public SqlBuilder( IGentleProvider provider, StatementType stmtType, Type type, LogicalOperator logicalOperator ) : base( provider != null ? new PersistenceBroker( provider ) : type != null ? new PersistenceBroker( type ) : null ) { this.provider = provider ?? broker.Provider; sf = this.provider.GetSqlFactory(); cmd = sf.GetCommand(); this.stmtType = stmtType; map = type != null ? ObjectFactory.GetMap( SessionBroker, type ) : null; this.logicalOperator = logicalOperator == LogicalOperator.Or ? " or " : " and "; fields = new ArrayList(); // list of fields quotedFields = new ArrayList(); // list of fields constraints = new ArrayList(); // list of string constraints parameterValues = new Hashtable(); parameterOrder = new ArrayList(); }
/// <summary> /// Build a persistence engine specific SQL statement for the given object class, using the /// class name as table name and properties for columns. /// </summary> /// <param name="stmtType">Type of statement to produce (select/insert/update/delete)</param> /// <param name="tableName">The table name to use in the constructed query or null to use /// the table name associated with the type</param> /// <param name="type">The object class/type</param> /// <param name="isCollection">For select statements, whether to return a list of items</param> /// <returns>An executable SQL statement object</returns> public SqlStatement GetStatement( StatementType stmtType, string tableName, Type type, bool isCollection ) { Check.Verify( stmtType != StatementType.Unknown, "You must set the StatementType before obtaining the SqlStatement." ); Check.Verify( map == null || map.Type == type, Error.DeveloperError, "The type for this statement has already been set." ); map = ObjectFactory.GetMap( SessionBroker, type ); this.stmtType = stmtType; if( tableName == null || tableName.Equals( String.Empty ) ) { SetTable( type ); } if( stmtType != StatementType.Count ) { // add key field(s) to select clause (and as parameter if not a collection) if( stmtType == StatementType.Select ) { AddKeyFields( type, ! isCollection ); } AddFields( type ); if( stmtType != StatementType.Insert && ! isCollection ) { bool isWithConcurrency = GentleSettings.ConcurrencyControl && ! isCollection; isWithConcurrency &= stmtType == StatementType.Update || stmtType == StatementType.Delete || stmtType == StatementType.SoftDelete; AddConstraints( type, isWithConcurrency ); } } // add filtering for soft deleted objects if( (stmtType == StatementType.Select || stmtType == StatementType.Count) && map.IsSoftDelete ) { AddConstraint( Operator.NotEquals, map.SoftDeleteMap.QuotedColumnName, -1 ); } // set any previously given parameter values on statement if( parameterOrder != null && parameterOrder.Count > 0 ) { int index = 0; foreach( string param in parameterOrder ) { object paramValue = parameterValues[ param ]; // Uwe Kitzmann: if data provider supports positional parameters only, // a call to AsParameter returns a "?" and not the parameter // IDataParameter dp = (IDataParameter) cmd.Parameters[ AsParameter( param ) ]; IDataParameter dp; // some provider libraries use the actual name (e.g. mysql) and // others add prefixes and suffixes (e.g. mssql) if( cmd.Parameters.Contains( param ) ) { dp = (IDataParameter) cmd.Parameters[ param ]; } else { string paramName = ParameterPrefix + param + ParameterSuffix; if( cmd.Parameters.Contains( paramName ) ) { dp = (IDataParameter) cmd.Parameters[ paramName ]; } else { dp = (IDataParameter) cmd.Parameters[ index++ ]; } } Check.VerifyNotNull( dp, Error.Unspecified, "Unable to locate parameter: " + param ); dp.Value = paramValue; } } return new SqlStatement( SessionBroker, stmtType, cmd, ToString(), type, rowLimit, rowOffset ); }
/// <summary> /// This method assumes that the tableName and identityColumn parameters are passed /// in the correct case. /// Please refer to the documentation of <see cref="GentleSqlFactory"/> for details on the /// purpose of this method. /// </summary> /// <param name="sql">The sql string to which we should append</param> /// <param name="om">An <see cref="Gentle.Framework.ObjectMap"/> instance of the object for which to retrieve the identity select</param> /// <returns>The modified sql string which also retrieves the identity value</returns> public override string GetIdentitySelect( string sql, ObjectMap om ) { return String.Format( "{0}{1} {2}", sql, GetStatementTerminator(), "select LAST_INSERT_ID()" ); }
/// <summary> /// This method assumes that the tableName and identityColumn parameters are passed /// in the correct case. /// Please refer to the documentation of <see cref="GentleSqlFactory"/> for details on the /// purpose of this method. /// </summary> /// <param name="sql">The sql string to which we should append</param> /// <param name="om">An <see cref="Gentle.Framework.ObjectMap"/> instance of the object for which to retrieve the identity select</param> /// <returns>The modified sql string which also retrieves the identity value</returns> public override string GetIdentitySelect( string sql, ObjectMap om ) { return sql + "; select last_insert_rowid()"; }
/// <summary> /// Construct a new SqlStatement instance of the specified type. The command object to be used /// for executing the query and the fully specified sql query string must be specified. /// </summary> /// <param name="broker">The PersistenceBroker instance to use for database access.</param> /// <param name="stmtType">The type of this statement.</param> /// <param name="cmd">The command object to use when executing the query.</param> /// <param name="sql">The fully specified sql query string.</param> /// <param name="type">The type of object being selected by this query or null if /// not applicable.</param> /// <param name="rowLimit">The maximum number of rows to be returned by this query /// or 0 for no limit.</param> /// <param name="rowOffset">The number of rows to be skipped by this statement. Note that /// for SQL Server this is applied after execution, whereas for other databases it is /// embedded in the SQL string.</param> public SqlStatement( PersistenceBroker broker, StatementType stmtType, IDbCommand cmd, string sql, Type type, int rowLimit, int rowOffset ) : base( broker, type ) { this.cmd = cmd; cmd.CommandText = sql; statementType = stmtType == StatementType.Unknown ? GetStatementType( sql ) : stmtType; map = type != null ? ObjectFactory.GetMap( broker, type ) : null; this.rowLimit = rowLimit; this.rowOffset = rowOffset; }
/// <summary> /// TODO FIXME /// This method breaks use of multiple brokers, as it is static and references Broker (and thus DefaultProvider). /// </summary> internal static object GetParameterValue( object val, ObjectMap map, FieldMap fm, StatementType stmtType ) { Check.VerifyNotNull( map, Error.NullParameter, "map" ); Check.VerifyNotNull( fm, Error.NullParameter, "fm" ); object result; // make a quick exit with the type name if the column is used for inheritance mapping if( map.InheritanceMap != null && fm.ColumnName == map.InheritanceMap.ColumnName ) { return map.Type.AssemblyQualifiedName; } // null handling if( fm.IsNull( val ) ) { // verify that null assignment isn't violating known defintions // dont check value types as they are never nullable, and allow null for autogenerated PKs if( val == null && ! fm.IsValueType && ! (fm.IsPrimaryKey && fm.IsAutoGenerated) ) { Check.Verify( fm.IsNullable, Error.NullProperty, fm.MemberName, map.Type ); } result = DBNull.Value; } else { // clip strings to make sure they fit target column if( fm.Type == typeof(string) && val != null && fm.Size > 0 && ((string) val).Length > fm.Size ) { result = ((string) val).Substring( 0, fm.Size ); } // clip DateTime values else if( fm.Type == typeof(DateTime) ) { // ensure that datetime values are clipped if outside database range result = EnsureValidDate( (DateTime) val ); } // convert enum values to numeric value else if( fm.Type.IsEnum ) { // use the integer or string value of the enum result = fm.HandleEnumAsString ? Convert.ChangeType( val, typeof(string) ) : Convert.ChangeType( val, Enum.GetUnderlyingType( fm.Type ) ); } // if column is autogenerated primary key use DBNull if an integer value of 0 is given else if( fm.IsAutoGeneratedKeyAndSupportedType && val.Equals( 0 ) && stmtType == StatementType.Insert ) { result = DBNull.Value; } else if( fm.Type == typeof(byte[]) || fm.Type == typeof(Byte[]) ) { MemoryStream stream = new MemoryStream( (byte[]) val ); result = stream.ToArray(); } else if( fm.Type == typeof(Guid) && fm.Size == 16 ) { result = TypeConverter.ToBinaryString( (Guid) val ); } else if( fm.Type == typeof(Guid) && Broker.ProviderName.StartsWith( "Sybase" ) ) { // Sybase needs the GUID as a string result = val.ToString(); } else // no special handling for other types/values { result = val; } } return result; }
/// <summary> /// Please refer to the documentation of <see cref="GentleSqlFactory"/> for details. /// </summary> public override string GetIdentitySelect( string sql, ObjectMap om ) { string seqName = (om.IdentityMap.SequenceName != null ? om.IdentityMap.SequenceName : (om.TableName + "_seq").ToUpper()); return String.Format( "select {0}.currval from dual", seqName ); }
private static Type GetColumnType( string columnName, ObjectMap map, SqlResult sr ) { Type result = null; if( map != null ) { FieldMap fm = map.GetFieldMapFromColumn( columnName ); if( fm != null ) { result = fm.Type; } } if( result == null && sr.RowsContained > 0 ) { object obj = sr.GetObject( 0, columnName ); if( obj != null ) { result = obj.GetType(); } } return result; }
/// <summary> /// This method updated the given ObjectMap instance with metadata obtained /// from the database. /// </summary> /// <param name="map">The ObjectMap to update</param> public virtual void UpdateObjectMap( ObjectMap map ) { if( level != AnalyzerLevel.None ) { TableMap dbMap = TableMaps[ map.TableName.ToLower() ] as TableMap; if( dbMap == null && level == AnalyzerLevel.OnDemand ) { Analyze( map.TableName ); dbMap = TableMaps[ map.TableName.ToLower() ] as TableMap; } Check.VerifyNotNull( dbMap, Error.UnknownTable, map.TableName, map.Type ); // make sure table name is set map.Provider = provider; // transfer table information map.IsView = dbMap.IsView; bool isUseData = ! map.IsView; isUseData |= map.IsView && HasCapability( ColumnInformation.IsView ); // transfer column information IList processedFields = new ArrayList( map.Fields.Count ); foreach( FieldMap dbfm in dbMap.Fields ) { try { FieldMap fm = map.Fields.FindColumn( dbfm.ColumnName ); if( fm != null ) { // view information if( map.IsView ) { if( HasCapability( ColumnInformation.ViewType ) ) { fm.SetDbType( dbfm.DbType ); } if( HasCapability( ColumnInformation.ViewSize ) ) { fm.SetSize( dbfm.Size ); } if( HasCapability( ColumnInformation.ViewIsNullable ) ) { fm.SetIsNullable( dbfm.IsNullable ); } if( HasCapability( ColumnInformation.ViewIsPrimaryKey ) ) { fm.SetIsPrimaryKey( dbfm.IsPrimaryKey ); } if( HasCapability( ColumnInformation.ViewIsAutoGenerated ) ) { fm.SetIsAutoGenerated( dbfm.IsAutoGenerated ); } if( HasCapability( ColumnInformation.ViewIsForeignKey ) && dbfm.IsForeignKey ) { fm.SetForeignKeyTableName( dbfm.ForeignKeyTableName ); fm.SetForeignKeyColumnName( dbfm.ForeignKeyColumnName ); } } else // table information { if( HasCapability( ColumnInformation.TableType ) ) { fm.SetDbType( dbfm.DbType ); } if( HasCapability( ColumnInformation.TableSize ) ) { fm.SetSize( dbfm.Size ); } if( HasCapability( ColumnInformation.TableIsNullable ) ) { fm.SetIsNullable( dbfm.IsNullable ); } if( HasCapability( ColumnInformation.TableIsPrimaryKey ) ) { fm.SetIsPrimaryKey( dbfm.IsPrimaryKey ); } if( HasCapability( ColumnInformation.TableIsAutoGenerated ) ) { fm.SetIsAutoGenerated( dbfm.IsAutoGenerated ); } if( HasCapability( ColumnInformation.TableIsForeignKey ) && dbfm.IsForeignKey ) { fm.SetForeignKeyTableName( dbfm.ForeignKeyTableName ); fm.SetForeignKeyColumnName( dbfm.ForeignKeyColumnName ); } } // remember which fields have been processed (for error reporting later) processedFields.Add( fm ); } } catch { // ignore the error but log a warning Check.LogWarning( LogCategories.Metadata, "Type {0} contains no mapping for column {1} in table {2}.", map.Type.Name, dbfm.ColumnName, map.TableName ); } } // verify that all defined fields had an existing column in the target table if( isUseData && processedFields.Count < map.Fields.Count ) { // construct informative error message StringBuilder sb = new StringBuilder(); bool single = processedFields.Count == map.Fields.Count - 1; sb.AppendFormat( "The column{0} ", single ? "" : "s" ); bool delimit = false; foreach( FieldMap fm in map.Fields ) { if( ! processedFields.Contains( fm ) ) { sb.AppendFormat( "{0}{1}", delimit ? ", " : "", fm.ColumnName ); delimit = true; } } sb.AppendFormat( " in table {0} do{1} not exist.", map.TableName, single ? "es" : "" ); // raise an exception Check.Fail( Error.DeveloperError, sb.ToString() ); } } }
/// <summary> /// Construct multiple objects of a given type from the data contained in the given SqlResult /// object. Refer to the Construct method of the <see cref="ObjectMap"/> class for details. /// </summary> /// <param name="type">The type of object to construct</param> /// <param name="sr">The SqlResult instance holding the data</param> /// <param name="result">An optional existing container in which to store the created objects. If /// this parameter is null a new IList instance will be created.</param> /// <returns>An IList holding the created objects</returns> /// <exception cref="GentleException"> will be raised if no object could be created</exception> public static IList GetCollection(Type type, SqlResult sr, IList result) { if (result == null) { result = MakeGenericList(type); } ObjectMap objectMap = GetMap(sr.SessionBroker, type); // check whether to store query result information bool isCache = GentleSettings.CacheObjects && GentleSettings.SkipQueryExecution && objectMap.CacheStrategy != CacheStrategy.Never; IList cache = isCache ? new ArrayList() : null; // remember keys of cached objects to permit SQE // process result set if (sr.RowsContained > 0) { ObjectMap actualMap = objectMap; int columnComboHashCode = 0; // cache constructor info for dynamic type construction Hashtable typeHash = null; Hashtable typeMaps = null; if (objectMap.InheritanceMap != null) { typeHash = new Hashtable(); typeMaps = new Hashtable(); } else // precompute fixed hash { columnComboHashCode = objectMap.DetermineConstructor(sr.ColumnNames, (object[])sr.Rows[0]); } // process result set for (int i = 0; i < sr.Rows.Count; i++) { object[] row = (object[])sr.Rows[i]; // dynamic object construction handling if (typeHash != null) { string assemblyQualifiedName = sr.GetString(i, objectMap.InheritanceMap.ColumnName); if (typeHash.ContainsKey(assemblyQualifiedName)) { columnComboHashCode = (int)typeHash[assemblyQualifiedName]; actualMap = (ObjectMap)typeMaps[assemblyQualifiedName]; } else { Type rowType = LoadType(assemblyQualifiedName); actualMap = GetMap(sr.SessionBroker, rowType); columnComboHashCode = actualMap.DetermineConstructor(sr.ColumnNames, (object[])sr.Rows[0]); typeMaps[assemblyQualifiedName] = actualMap; typeHash[assemblyQualifiedName] = columnComboHashCode; } } // skip non-derived classes for dynamic types if (actualMap.Type == objectMap.Type || actualMap.Type.IsSubclassOf(objectMap.Type)) { object obj = actualMap.Construct(columnComboHashCode, row, sr.SessionBroker); if (obj is IEntity) { (obj as IEntity).IsPersisted = true; } result.Add(obj); // cache result if necessary if (isCache) { cache.Add(actualMap.GetInstanceHashKey(obj)); } } } } if (isCache) { CacheManager.Insert(sr.Statement.CacheKey, cache); } return(result); }