#region Apache Notice /***************************************************************************** * $Revision: 374175 $ * $LastChangedDate$ * $LastChangedBy$ * * iBATIS.NET Data Mapper * Copyright (C) 2008/2005 - The Apache Software Foundation * * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ********************************************************************************/ #endregion using System; using System.Collections; using System.Collections.Specialized; using System.Data; using Apache.Ibatis.Common; using Apache.Ibatis.DataMapper.Exceptions; namespace Apache.Ibatis.DataMapper.Data { /// /// An implementation of that will copy the contents /// of the an open to an in-memory if the /// session doesn't allow multiple open with /// the same . /// public class InMemoryDataReader : IDataReader { private int _currentRowIndex = 0; private int _currentResultIndex = 0; private bool _isClosed = false; private InMemoryResultSet[ ] _results = null; /// /// Creates an InMemoryDataReader from a /// /// The which holds the records from the Database. public InMemoryDataReader(IDataReader reader) { ArrayList resultList = new ArrayList(); try { _currentResultIndex = 0; _currentRowIndex = 0; resultList.Add( new InMemoryResultSet( reader, true ) ); while( reader.NextResult() ) { resultList.Add( new InMemoryResultSet( reader, false ) ); } _results = ( InMemoryResultSet[ ] ) resultList.ToArray( typeof( InMemoryResultSet ) ); } catch( Exception e ) { throw new DataMapperException( "There was a problem converting an IDataReader to an InMemoryDataReader", e ); } finally { reader.Close(); reader.Dispose(); } } #region IDataReader Members /// /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. /// public int RecordsAffected { get { throw new NotImplementedException( "InMemoryDataReader only used for select IList statements !" ); } } /// /// Gets a value indicating whether the data reader is closed. /// public bool IsClosed { get { return _isClosed; } } /// /// Advances the data reader to the next result, when reading the results of batch SQL statements. /// /// public bool NextResult() { _currentResultIndex++; if( _currentResultIndex >= _results.Length ) { _currentResultIndex--; return false; } return true; } /// /// Closes the IDataReader 0bject. /// public void Close() { _isClosed = true; } /// /// Advances the IDataReader to the next record. /// /// true if there are more rows; otherwise, false. public bool Read() { _currentRowIndex++; if( _currentRowIndex >= _results[ _currentResultIndex ].RecordCount ) { _currentRowIndex--; return false; } return true; } /// /// Gets a value indicating the depth of nesting for the current row. /// public int Depth { get { return _currentResultIndex; } } /// /// Returns a DataTable that describes the column metadata of the IDataReader. /// /// public DataTable GetSchemaTable() { throw new NotImplementedException( "GetSchemaTable() is not implemented, cause not use." ); } #endregion #region IDisposable Members /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { _isClosed = true; _results = null; } #endregion #region IDataRecord Members /// /// Gets the 32-bit signed integer value of the specified field. /// /// The zero-based column ordinal. /// The value of the column. public int GetInt32(int fieldIndex) { return (int) GetValue(fieldIndex); } /// /// Gets the column with the specified name. /// public object this[string name] { get { return this [GetOrdinal(name)]; } } /// /// Gets the column located at the specified index. /// public object this[int fieldIndex] { get { return GetValue( fieldIndex ); } } /// /// Return the value of the specified field. /// /// The index of the field to find. /// The object which will contain the field value upon return. public object GetValue(int fieldIndex) { return this.CurrentResultSet.GetValue( _currentRowIndex, fieldIndex ); } /// /// Return whether the specified field is set to null. /// /// The zero-based column ordinal. /// The value of the column. public bool IsDBNull(int fieldIndex) { return (GetValue(fieldIndex) == DBNull.Value); } /// /// Reads a stream of bytes from the specified column offset into the buffer as an array, /// starting at the given buffer offset. /// /// The zero-based column ordinal. /// The index within the field from which to begin the read operation. /// The buffer into which to read the stream of bytes. /// The index for buffer to begin the read operation. /// The number of bytes to read. /// The actual number of bytes read. public long GetBytes(int fieldIndex, long dataIndex, byte[] buffer, int bufferIndex, int length) { object value = GetValue(fieldIndex); if (!(value is byte [])) { throw new InvalidCastException ("Type is " + value.GetType ().ToString ()); } if ( buffer == null ) { // Return length of data return ((byte []) value).Length; } else { // Copy data into buffer int availableLength = (int) ( ( (byte []) value).Length - dataIndex); if (availableLength < length) { length = availableLength; } Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length); return length; } } /// /// Gets the 8-bit unsigned integer value of the specified column. /// /// The zero-based column ordinal. /// The value of the column. public byte GetByte(int fieldIndex) { return (byte) GetValue(fieldIndex); } /// /// Gets the Type information corresponding to the type of Object that would be returned from GetValue. /// /// The zero-based column ordinal. /// The value of the column. public Type GetFieldType(int fieldIndex) { return this.CurrentResultSet.GetFieldType(fieldIndex); } /// /// Gets the fixed-position numeric value of the specified field. /// /// The zero-based column ordinal. /// The value of the column. public decimal GetDecimal(int fieldIndex) { return (decimal) GetValue(fieldIndex); } /// /// Gets all the attribute fields in the collection for the current record. /// /// /// public int GetValues(object[] values) { return this.CurrentResultSet.GetValues( _currentRowIndex, values ); } /// /// Gets the name for the field to find. /// /// The zero-based column ordinal. /// The value of the column. public string GetName(int fieldIndex) { return this.CurrentResultSet.GetName( fieldIndex ); } /// /// Indicates the number of fields within the current record. This property is read-only. /// public int FieldCount { get { return this.CurrentResultSet.FieldCount; } } /// /// /// /// The zero-based column ordinal. /// The value of the column. public long GetInt64(int fieldIndex) { return (long) GetValue(fieldIndex); } /// /// /// /// The zero-based column ordinal. /// The value of the column. public double GetDouble(int fieldIndex) { return (double) GetValue(fieldIndex); } /// /// Gets the value of the specified column as a Boolean. /// /// The zero-based column ordinal. /// The value of the column. public bool GetBoolean(int fieldIndex) { return (bool) GetValue(fieldIndex); } /// /// Returns the GUID value of the specified field. /// /// The zero-based column ordinal. /// The value of the column. public Guid GetGuid(int fieldIndex) { return (Guid) GetValue(fieldIndex); } /// /// Returns the value of the specified column as a DateTime object. /// /// The zero-based column ordinal. /// The value of the column. public DateTime GetDateTime(int fieldIndex) { return (DateTime) GetValue(fieldIndex); } /// /// Returns the column ordinal, given the name of the column. /// /// The name of the column. /// The value of the column. public int GetOrdinal(string colName) { return this.CurrentResultSet.GetOrdinal(colName); } /// /// Gets the database type information for the specified field. /// /// The index of the field to find. /// The database type information for the specified field. public string GetDataTypeName(int fieldIndex) { return this.CurrentResultSet.GetDataTypeName(fieldIndex); } /// /// Returns the value of the specified column as a single-precision floating point number. /// /// The zero-based column ordinal. /// The value of the column. public float GetFloat(int fieldIndex) { return (float) GetValue(fieldIndex); } /// /// Gets an IDataReader to be used when the field points to more remote structured data. /// /// The zero-based column ordinal. /// The value of the column. public IDataReader GetData(int fieldIndex) { throw new NotImplementedException( "GetData(int) is not implemented, cause not use." ); } /// /// Reads a stream of characters from the specified column offset into the buffer as an array, /// starting at the given buffer offset. /// /// The zero-based column ordinal. /// The index within the row from which to begin the read operation. /// The buffer into which to read the stream of bytes. /// The index for buffer to begin the read operation. /// The number of bytes to read. /// The actual number of characters read. public long GetChars(int fieldIndex, long dataIndex, char[] buffer, int bufferIndex, int length) { object value = GetValue(fieldIndex); char [] valueBuffer = null; if (value is char[]) { valueBuffer = (char[])value; } else if (value is string) { valueBuffer = ((string)value).ToCharArray(); } else { throw new InvalidCastException ("Type is " + value.GetType ().ToString ()); } if ( buffer == null ) { // Return length of data return valueBuffer.Length; } else { // Copy data into buffer Array.Copy (valueBuffer, (int) dataIndex, buffer, bufferIndex, length); return valueBuffer.Length - dataIndex; } } /// /// Gets the string value of the specified field. /// /// The zero-based column ordinal. /// The value of the column. public string GetString(int fieldIndex) { return (string) GetValue(fieldIndex); } /// /// Gets the character value of the specified column. /// /// The zero-based column ordinal. /// The value of the column. public char GetChar(int fieldIndex) { return (char) GetValue(fieldIndex); } /// /// Gets the 16-bit signed integer value of the specified field. /// /// The zero-based column ordinal. /// The value of the column. public short GetInt16(int fieldIndex) { return (short)GetValue (fieldIndex); } #endregion /// /// Gets the current result set. /// /// The current result set. private InMemoryResultSet CurrentResultSet { get {return _results[ _currentResultIndex ];} } /// /// Represent an in-memory result set /// private class InMemoryResultSet { // [row][column] private readonly object[ ][ ] _records = null; private int _fieldCount = 0; private string[] _fieldsName = null; private Type[] _fieldsType = null; StringDictionary _fieldsNameLookup = new StringDictionary(); private string[] _dataTypeName = null; /// /// Creates an in-memory ResultSet from a /// /// /// true if the is already positioned on the record /// to start reading from. /// /// The which holds the records from the Database. public InMemoryResultSet(IDataReader reader, bool isMidstream ) { // [record index][ columns values=object[ ] ] ArrayList recordsList = new ArrayList(); _fieldCount = reader.FieldCount; _fieldsName = new string[_fieldCount]; _fieldsType = new Type[_fieldCount]; _dataTypeName = new string[_fieldCount]; bool firstRow = true; // if we are in the middle of processing the reader then don't bother // to move to the next record - just use the current one. // Copy the records in memory while( isMidstream || reader.Read()) { if( firstRow ) { for( int fieldIndex = 0; fieldIndex < reader.FieldCount; fieldIndex++ ) { string fieldName = reader.GetName( fieldIndex ); _fieldsName[ fieldIndex ] = fieldName; if (!_fieldsNameLookup.ContainsKey(fieldName)) { _fieldsNameLookup.Add(fieldName, fieldIndex.ToString() ); } _fieldsType[fieldIndex] = reader.GetFieldType( fieldIndex ) ; _dataTypeName[ fieldIndex ] = reader.GetDataTypeName( fieldIndex ); } } firstRow = false; object[ ] columnsValues = new object[_fieldCount]; reader.GetValues( columnsValues ); recordsList.Add( columnsValues ); isMidstream = false; } _records = ( object[ ][ ] ) recordsList.ToArray( typeof( object[ ] ) ); } /// /// Gets the number of columns in the current row. /// /// The number of columns in the current row. public int FieldCount { get {return _fieldCount;} } /// /// Get a column value in a row /// /// The row index /// The column index /// The column value public object GetValue( int rowIndex, int colIndex ) { return _records[ rowIndex ][ colIndex ]; } /// /// The number of record contained in the ResultSet. /// public int RecordCount { get { return _records.Length; } } /// /// Gets the type of the field. /// /// Index of the col. /// The type of the field. public Type GetFieldType( int colIndex ) { return _fieldsType[ colIndex ]; } /// /// Gets the name of the field. /// /// Index of the col. /// The name of the field. public string GetName( int colIndex ) { return _fieldsName[ colIndex ]; } /// /// Gets the ordinal. /// /// Name of the column. /// The ordinal of the column public int GetOrdinal( string colName ) { if( _fieldsNameLookup.ContainsKey(colName) ) { return Convert.ToInt32(_fieldsNameLookup[ colName ]); } else { throw new IndexOutOfRangeException( String.Format( "No column with the specified name was found: {0}.", colName ) ); } } /// /// Gets the name of the database type. /// /// Index of the col. /// The name of the database type public string GetDataTypeName( int colIndex ) { return _dataTypeName[ colIndex ]; } /// /// Gets the values. /// /// Index of the row. /// The values. /// public int GetValues( int rowIndex, object[ ] values ) { Array.Copy( _records[ rowIndex ], 0, values, 0, _fieldCount ); return _fieldCount; } } } }