BridgeDataRecord.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Query / ResultAssembly / BridgeDataRecord.cs / 1305376 / BridgeDataRecord.cs

                            //------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// @owner  [....] 
// @backupOwner [....]
//--------------------------------------------------------------------- 
 
namespace System.Data.Query.ResultAssembly {
 
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data; 
    using System.Data.Common;
    using System.Data.Common.CommandTrees; 
    using System.Data.Common.Internal.Materialization; 
    using System.Data.Metadata.Edm;
    using System.Data.Query.PlanCompiler; 
    using System.Diagnostics;
    using System.Text;
    using System.Threading;
 
    /// 
    /// DbDataRecord functionality for the bridge. 
    ///  
    sealed internal class BridgeDataRecord : DbDataRecord, IExtendedDataRecord {
 
        #region state

        /// 
        /// How deep down the hierarchy are we? 
        /// 
        internal readonly int Depth; 
 
        /// 
        /// Where the data comes from 
        /// 
        private readonly Shaper Shaper;

        ///  
        /// The current record that we're responsible for; this will change from row to row
        /// on the source data reader.  Will be set to null when parent the enumerator has 
        /// returned false. 
        /// 
        private RecordState _source; 

        /// 
        /// Current state of the record;
        ///  
        private Status _status;
        private enum Status { 
            Open = 0, 
            ClosedImplicitly = 1,
            ClosedExplicitly = 2, 
        };

        /// 
        /// the column ordinal of the last column read, used to enforce sequential access 
        /// 
        private int _lastColumnRead; 
 
        /// 
        /// the last data offset of a chunking read returned, used to enforce sequential access 
        /// 
        private long _lastDataOffsetRead;

        ///  
        /// the last ordinal that IsDBNull was called for; used to avoid re-reading the value;
        ///  
        private int _lastOrdinalCheckedForNull; 

        ///  
        /// value, of the last column that IsDBNull was called for; used to avoid re-reading the value;
        /// 
        private object _lastValueCheckedForNull;
 
        /// 
        /// Set to the current data record when we hand them out.  (For data reader columns, 
        /// we use it's attached data record) The Close, GetValue and Read methods ensures 
        /// that this is implicitly closed when we move past it.
        ///  
        private BridgeDataReader _currentNestedReader;
        private BridgeDataRecord _currentNestedRecord;

        #endregion 

        #region constructors 
 
        internal BridgeDataRecord(Shaper shaper, int depth)
            : base() { 
            Debug.Assert(null != shaper, "null shaper?");
            Shaper = shaper;
            Depth = depth;
            // Rest of state is set through the SetRecordSource method. 
        }
 
        #endregion 

        #region state management 

        /// 
        /// Called by our owning datareader when it is explicitly closed; will
        /// not be called for nested structures, they go through the ClosedImplicitly. 
        /// path instead.
        ///  
        internal void CloseExplicitly() { 
            _status = Status.ClosedExplicitly;
            _source = null; // can't have data any longer once we're closed. 
            CloseNestedObjectImplicitly();
        }

        ///  
        /// Called by our parent object to ensure that we're marked as implicitly
        /// closed;  will not be called for root level data readers. 
        ///  
        internal void CloseImplicitly() {
            _status = Status.ClosedImplicitly; 
            _source = null; // can't have data any longer once we're closed.
            CloseNestedObjectImplicitly();
        }
 
        /// 
        /// Ensure that whatever column we're currently processing is implicitly closed; 
        ///  
        private void CloseNestedObjectImplicitly() {
            // it would be nice to use Interlocked.Exchange to avoid multi-thread `race condition risk 
            // when the the bridge is being misused by the user accessing it with multiple threads.
            // but this is called frequently enough to have a performance impact
            BridgeDataRecord currentNestedRecord = _currentNestedRecord;
            if (null != currentNestedRecord) { 
                _currentNestedRecord = null;
                currentNestedRecord.CloseImplicitly(); 
            } 
            BridgeDataReader currentNestedReader = _currentNestedReader;
            if (null != currentNestedReader) { 
                _currentNestedReader = null;
                currentNestedReader.CloseImplicitly();
            }
        } 

        ///  
        /// Should be called after each Read on the data reader. 
        /// 
        internal void SetRecordSource(RecordState newSource, bool hasData) { 
            Debug.Assert(null == _currentNestedRecord, "didn't close the nested record?");
            Debug.Assert(null == _currentNestedReader, "didn't close the nested reader?");

            // A peculiar behavior of IEnumerator is that when MoveNext() returns 
            // false, the Current still points to the last value, which is not
            // what we really want to reflect here. 
            if (hasData) { 
                Debug.Assert(null != newSource, "hasData but null newSource?"); // this shouldn't happen...
                _source = newSource; 
            }
            else {
                _source = null;
            } 
            _status = Status.Open;
 
            _lastColumnRead = -1; 
            _lastDataOffsetRead = -1;
            _lastOrdinalCheckedForNull = -1; 
            _lastValueCheckedForNull = null;
        }

        #endregion 

        #region assertion helpers 
 
        /// 
        /// Ensures that the reader is actually open, and throws an exception if not 
        /// 
        private void AssertReaderIsOpen() {
            if (IsExplicitlyClosed) {
                throw EntityUtil.ClosedDataReaderError(); 
            }
            if (IsImplicitlyClosed) { 
                throw EntityUtil.ImplicitlyClosedDataReaderError(); 
            }
        } 

        /// 
        /// Helper method.
        ///  
        private void AssertReaderIsOpenWithData() {
            AssertReaderIsOpen(); 
 
            if (!HasData) {
                throw EntityUtil.NoData(); 
            }
        }

        ///  
        /// Ensures that sequential access rules are being obeyed for non-chunking
        /// getter methods, throws the appropriate exception if not.  Also ensures 
        /// that the last column/chunk is set appropriately. 
        /// 
        ///  
        private void AssertSequentialAccess(int ordinal) {
            Debug.Assert(null != _source, "null _source?"); // we should have already called AssertReaderIsOpen.

            if (ordinal < 0 || ordinal >= _source.ColumnCount) { 
                throw EntityUtil.ArgumentOutOfRange("ordinal");
            } 
            if (_lastColumnRead >= ordinal) { 
                throw EntityUtil.NonSequentialColumnAccess(ordinal, _lastColumnRead + 1);
            } 
            _lastColumnRead = ordinal;
            // SQLBUDT #442001 -- we need to mark things that are not using GetBytes/GetChars
            //                    in a way that prevents them from being read a second time
            //                    using those methods.  Pointing past any potential data is 
            //                    how we do that.
            _lastDataOffsetRead = long.MaxValue; 
        } 

        ///  
        /// Ensures that sequential access rules are being obeyed for chunking
        /// getter methods, throws the appropriate exception if not.  Also ensures
        /// that the last column/chunk is set appropriately.
        ///  
        /// 
        ///  
        ///  
        private void AssertSequentialAccess(int ordinal, long dataOffset, string methodName) {
            Debug.Assert(null != _source, "null _source?"); // we should have already called AssertReaderIsOpen. 

            if (ordinal < 0 || ordinal >= _source.ColumnCount) {
                throw EntityUtil.ArgumentOutOfRange("ordinal");
            } 
            if (_lastColumnRead > ordinal || (_lastColumnRead == ordinal && _lastDataOffsetRead == long.MaxValue)) {
                throw EntityUtil.NonSequentialColumnAccess(ordinal, _lastColumnRead + 1); 
            } 
            if (_lastColumnRead == ordinal) {
                if (_lastDataOffsetRead >= dataOffset) { 
                    throw EntityUtil.NonSequentialChunkAccess(dataOffset, _lastDataOffsetRead + 1, methodName);
                }
                // _lastDataOffsetRead will be set by GetBytes/GetChars, since we need to set it
                // to the last offset that was actually read, which isn't necessarily what was 
                // requested.
            } 
            else { 
                // Doin' a new thang...
                _lastColumnRead = ordinal; 
                _lastDataOffsetRead = -1;
            }
        }
 
        /// 
        /// True when the record has data (SetRecordSource was called with true) 
        ///  
        internal bool HasData {
            get { 
                bool result = (_source != null);
                return result;
            }
        } 

        ///  
        /// True so long as we haven't been closed either implicity or explictly 
        /// 
        internal bool IsClosed { 
            get {
                return (_status != Status.Open);
            }
        } 

        ///  
        /// Determine whether we have been explicitly closed by our owning 
        /// data reader; only data records that are responsible for processing
        /// data reader requests can be explicitly closed; 
        /// 
        internal bool IsExplicitlyClosed {
            get {
                return (_status == Status.ClosedExplicitly); 
            }
        } 
 
        /// 
        /// Determine whether the parent data reader or record moved on from 
        /// where we can be considered open, (because the consumer of the
        /// parent data reader/record called either the GetValue() or Read()
        /// methods on the parent);
        ///  
        internal bool IsImplicitlyClosed {
            get { 
                return (_status == Status.ClosedImplicitly); 
            }
        } 
        #endregion

        #region metadata properties and methods
 
        /// 
        /// implementation of DbDataRecord.DataRecordInfo property 
        ///  
        public DataRecordInfo DataRecordInfo {
            get { 
                AssertReaderIsOpen();
                DataRecordInfo result = _source.DataRecordInfo;
                return result;
            } 
        }
 
        ///  
        /// implementation of DbDataRecord.FieldCount property
        ///  
        override public int FieldCount {
            get {
                AssertReaderIsOpen();
                return _source.ColumnCount; 
            }
        } 
 
        /// 
        /// Helper method to get the edm TypeUsage for the specified column; 
        ///
        /// If the column requested is a record, we'll pick up whatever the
        /// current record says it is, otherwise we'll take whatever was stored
        /// on our record state. 
        /// 
        ///  
        ///  
        private TypeUsage GetTypeUsage(int ordinal) {
            // Some folks are picky about the exception we throw 
            if (ordinal < 0 || ordinal >= _source.ColumnCount) {
                throw EntityUtil.ArgumentOutOfRange("ordinal");
            }
            TypeUsage result; 

            // 
            RecordState recordState = _source.CurrentColumnValues[ordinal] as RecordState; 
            if (null != recordState) {
                result = recordState.DataRecordInfo.RecordType; 
            }
            else {
                result = _source.GetTypeUsage(ordinal);
            } 
            return result;
        } 
 
        /// 
        /// implementation of DbDataRecord.GetDataTypeName() method 
        /// 
        /// 
        /// 
        override public string GetDataTypeName(int ordinal) { 
            AssertReaderIsOpenWithData();
            return TypeHelpers.GetFullName(GetTypeUsage(ordinal)); 
        } 

        ///  
        /// implementation of DbDataRecord.GetFieldType() method
        /// 
        /// 
        ///  
        override public Type GetFieldType(int ordinal) {
            AssertReaderIsOpenWithData(); 
            return BridgeDataReader.GetClrTypeFromTypeMetadata(GetTypeUsage(ordinal)); 
        }
 
        /// 
        /// implementation of DbDataRecord.GetName() method
        /// 
        ///  
        /// 
        override public string GetName(int ordinal) { 
            AssertReaderIsOpen(); 
            return _source.GetName(ordinal);
        } 

        /// 
        /// implementation of DbDataRecord.GetOrdinal() method
        ///  
        /// 
        ///  
        override public int GetOrdinal(string name) { 
            AssertReaderIsOpen();
            return _source.GetOrdinal(name); 
        }

        #endregion
 
        #region general getter methods and indexer properties
 
        ///  
        /// implementation for DbDataRecord[ordinal] indexer property
        ///  
        /// 
        /// 
        override public object this[int ordinal] {
            get { 
                return GetValue(ordinal);
            } 
        } 

        ///  
        /// implementation for DbDataRecord[name] indexer property
        /// 
        /// 
        ///  
        override public object this[string name] {
            get { 
                return GetValue(GetOrdinal(name)); 
            }
        } 

        /// 
        /// implementation for DbDataRecord.GetValue() method
        /// 
        /// This method is used by most of the column getters on this
        /// class to retrieve the value from the source reader.  Therefore, 
        /// it asserts all the good things, like that the reader is open, 
        /// and that it has data, and that you're not trying to circumvent
        /// sequential access requirements. 
        /// 
        /// 
        /// 
        override public Object GetValue(int ordinal) { 
            AssertReaderIsOpenWithData();
            AssertSequentialAccess(ordinal); 
 
            object result = null;
 
            if (ordinal == _lastOrdinalCheckedForNull) {
                result = _lastValueCheckedForNull;
            }
            else { 
                _lastOrdinalCheckedForNull = -1;
                _lastValueCheckedForNull = null; 
 
                CloseNestedObjectImplicitly();
 
                result = _source.CurrentColumnValues[ordinal];

                // If we've got something that's nested, then make sure we
                // update the current nested record with it so we can be certain 
                // to close it implicitly when we move past it.
                if (_source.IsNestedObject(ordinal)) { 
                    result = GetNestedObjectValue(result); 
                }
            } 
            return result;
        }

        ///  
        /// For nested objects (records/readers) we have a bit more work to do; this
        /// method extracts it all out from the main GetValue method so it doesn't 
        /// have to be so big. 
        /// 
        ///  
        /// 
        private object GetNestedObjectValue(object result) {
            if (result != DBNull.Value) {
                RecordState recordState = result as RecordState; 
                if (null != recordState) {
                    if (recordState.IsNull) { 
                        result = DBNull.Value; 
                    }
                    else { 
                        BridgeDataRecord nestedRecord = new BridgeDataRecord(Shaper, Depth + 1);
                        nestedRecord.SetRecordSource(recordState, true);
                        result = nestedRecord;
                        _currentNestedRecord = nestedRecord; 
                        _currentNestedReader = null;
                    } 
                } 
                else {
                    Coordinator coordinator = result as Coordinator; 
                    if (null != coordinator) {
                        BridgeDataReader nestedReader = new BridgeDataReader(Shaper, coordinator.TypedCoordinatorFactory, Depth + 1);
                        result = nestedReader;
                        _currentNestedRecord = null; 
                        _currentNestedReader = nestedReader;
                    } 
                    else { 
                        Debug.Fail("unexpected type of nested object result: " + result.GetType().ToString());
                    } 
                }
            }
            return result;
        } 

        ///  
        /// implementation for DbDataRecord.GetValues() method 
        /// 
        ///  
        /// 
        override public int GetValues(object[] values) {
            EntityUtil.CheckArgumentNull(values, "values");
 
            int copy = Math.Min(values.Length, FieldCount);
            for (int i = 0; i < copy; ++i) { 
                values[i] = GetValue(i); 
            }
            return copy; 
        }

        #endregion
 
        #region simple scalar value getter methods
 
        ///  
        /// implementation of DbDataRecord.GetBoolean() method
        ///  
        /// 
        /// 
        override public bool GetBoolean(int ordinal) {
            return (bool)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetByte() method
        ///  
        /// 
        /// 
        override public byte GetByte(int ordinal) {
            return (byte)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetChar() method
        ///  
        /// 
        /// 
        override public char GetChar(int ordinal) {
            return (char)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetDateTime() method
        ///  
        /// 
        /// 
        override public DateTime GetDateTime(int ordinal) {
            return (DateTime)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetDecimal() method
        ///  
        /// 
        /// 
        override public Decimal GetDecimal(int ordinal) {
            return (Decimal)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetDouble() method
        ///  
        /// 
        /// 
        override public double GetDouble(int ordinal) {
            return (double)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetFloat() method
        ///  
        /// 
        /// 
        override public float GetFloat(int ordinal) {
            return (float)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetGuid() method
        ///  
        /// 
        /// 
        override public Guid GetGuid(int ordinal) {
            return (Guid)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetInt16() method
        ///  
        /// 
        /// 
        override public Int16 GetInt16(int ordinal) {
            return (Int16)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetInt32() method
        ///  
        /// 
        /// 
        override public Int32 GetInt32(int ordinal) {
            return (Int32)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetInt64() method
        ///  
        /// 
        /// 
        override public Int64 GetInt64(int ordinal) {
            return (Int64)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetString() method
        ///  
        /// 
        /// 
        override public String GetString(int ordinal) {
            return (String)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.IsDBNull() method
        ///  
        /// 
        /// 
        override public bool IsDBNull(int ordinal) {
            // This seems like a hack, but the the problem is that I need 
            // to make sure I don't monkey with caching things, and if I
            // call IsDBNull directly on the store reader, I'll potentially 
            // lose data because I'm expecting SequentialAccess rules. 

            object columnValue = GetValue(ordinal); 

            // Need to backup one because we technically didn't read the
            // value yet but the GetValue method advanced our pointer to
            // what the value was.  Another hack, but it's way less code 
            // than trying to avoid advancing to begin with.
            _lastColumnRead--; 
            _lastDataOffsetRead = -1; 

            // So as to avoid reconstructing nested records, readers, and 
            // rereading data from the iterator source cache, we just cache
            // the value we read and the ordinal it came from, so if someone
            // is doing the right thing(TM) and calling IsDBNull before calling
            // GetValue, we won't construct another one. 
            _lastValueCheckedForNull = columnValue;
            _lastOrdinalCheckedForNull = ordinal; 
 
            bool result = (DBNull.Value == columnValue);
 
            return result;
        }

        #endregion 

        #region chunking scalar value getter methods 
 
        /// 
        /// implementation for DbDataRecord.GetBytes() method 
        /// 
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        override public long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) {
            AssertReaderIsOpenWithData(); 
            AssertSequentialAccess(ordinal, dataOffset, "GetBytes");

            long result = _source.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
 
            if (buffer != null) {
                _lastDataOffsetRead = dataOffset + result - 1; // just what was read, nothing more. 
            } 
            return result;
        } 

        /// 
        /// implementation for DbDataRecord.GetChars() method
        ///  
        /// 
        ///  
        ///  
        /// 
        ///  
        /// 
        override public long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) {
            AssertReaderIsOpenWithData();
            AssertSequentialAccess(ordinal, dataOffset, "GetChars"); 

            long result = _source.GetChars(ordinal, dataOffset, buffer, bufferOffset, length); 
 
            if (buffer != null) {
                _lastDataOffsetRead = dataOffset + result - 1; // just what was read, nothing more. 
            }
            return result;
        }
 
        #endregion
 
        #region complex type getters 

        ///  
        /// implementation for DbDataRecord.GetData() method
        /// 
        /// 
        ///  
        override protected DbDataReader GetDbDataReader(int ordinal) {
            return (DbDataReader)GetValue(ordinal); 
        } 

        ///  
        /// implementation for DbDataRecord.GetDataRecord() method
        /// 
        /// 
        ///  
        public DbDataRecord GetDataRecord(int ordinal) {
            return (DbDataRecord)GetValue(ordinal); 
        } 

        ///  
        /// Used to return a nested result
        /// 
        /// 
        ///  
        public DbDataReader GetDataReader(int ordinal) {
            return this.GetDbDataReader(ordinal); 
        } 

        #endregion 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// @owner  [....] 
// @backupOwner [....]
//--------------------------------------------------------------------- 
 
namespace System.Data.Query.ResultAssembly {
 
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data; 
    using System.Data.Common;
    using System.Data.Common.CommandTrees; 
    using System.Data.Common.Internal.Materialization; 
    using System.Data.Metadata.Edm;
    using System.Data.Query.PlanCompiler; 
    using System.Diagnostics;
    using System.Text;
    using System.Threading;
 
    /// 
    /// DbDataRecord functionality for the bridge. 
    ///  
    sealed internal class BridgeDataRecord : DbDataRecord, IExtendedDataRecord {
 
        #region state

        /// 
        /// How deep down the hierarchy are we? 
        /// 
        internal readonly int Depth; 
 
        /// 
        /// Where the data comes from 
        /// 
        private readonly Shaper Shaper;

        ///  
        /// The current record that we're responsible for; this will change from row to row
        /// on the source data reader.  Will be set to null when parent the enumerator has 
        /// returned false. 
        /// 
        private RecordState _source; 

        /// 
        /// Current state of the record;
        ///  
        private Status _status;
        private enum Status { 
            Open = 0, 
            ClosedImplicitly = 1,
            ClosedExplicitly = 2, 
        };

        /// 
        /// the column ordinal of the last column read, used to enforce sequential access 
        /// 
        private int _lastColumnRead; 
 
        /// 
        /// the last data offset of a chunking read returned, used to enforce sequential access 
        /// 
        private long _lastDataOffsetRead;

        ///  
        /// the last ordinal that IsDBNull was called for; used to avoid re-reading the value;
        ///  
        private int _lastOrdinalCheckedForNull; 

        ///  
        /// value, of the last column that IsDBNull was called for; used to avoid re-reading the value;
        /// 
        private object _lastValueCheckedForNull;
 
        /// 
        /// Set to the current data record when we hand them out.  (For data reader columns, 
        /// we use it's attached data record) The Close, GetValue and Read methods ensures 
        /// that this is implicitly closed when we move past it.
        ///  
        private BridgeDataReader _currentNestedReader;
        private BridgeDataRecord _currentNestedRecord;

        #endregion 

        #region constructors 
 
        internal BridgeDataRecord(Shaper shaper, int depth)
            : base() { 
            Debug.Assert(null != shaper, "null shaper?");
            Shaper = shaper;
            Depth = depth;
            // Rest of state is set through the SetRecordSource method. 
        }
 
        #endregion 

        #region state management 

        /// 
        /// Called by our owning datareader when it is explicitly closed; will
        /// not be called for nested structures, they go through the ClosedImplicitly. 
        /// path instead.
        ///  
        internal void CloseExplicitly() { 
            _status = Status.ClosedExplicitly;
            _source = null; // can't have data any longer once we're closed. 
            CloseNestedObjectImplicitly();
        }

        ///  
        /// Called by our parent object to ensure that we're marked as implicitly
        /// closed;  will not be called for root level data readers. 
        ///  
        internal void CloseImplicitly() {
            _status = Status.ClosedImplicitly; 
            _source = null; // can't have data any longer once we're closed.
            CloseNestedObjectImplicitly();
        }
 
        /// 
        /// Ensure that whatever column we're currently processing is implicitly closed; 
        ///  
        private void CloseNestedObjectImplicitly() {
            // it would be nice to use Interlocked.Exchange to avoid multi-thread `race condition risk 
            // when the the bridge is being misused by the user accessing it with multiple threads.
            // but this is called frequently enough to have a performance impact
            BridgeDataRecord currentNestedRecord = _currentNestedRecord;
            if (null != currentNestedRecord) { 
                _currentNestedRecord = null;
                currentNestedRecord.CloseImplicitly(); 
            } 
            BridgeDataReader currentNestedReader = _currentNestedReader;
            if (null != currentNestedReader) { 
                _currentNestedReader = null;
                currentNestedReader.CloseImplicitly();
            }
        } 

        ///  
        /// Should be called after each Read on the data reader. 
        /// 
        internal void SetRecordSource(RecordState newSource, bool hasData) { 
            Debug.Assert(null == _currentNestedRecord, "didn't close the nested record?");
            Debug.Assert(null == _currentNestedReader, "didn't close the nested reader?");

            // A peculiar behavior of IEnumerator is that when MoveNext() returns 
            // false, the Current still points to the last value, which is not
            // what we really want to reflect here. 
            if (hasData) { 
                Debug.Assert(null != newSource, "hasData but null newSource?"); // this shouldn't happen...
                _source = newSource; 
            }
            else {
                _source = null;
            } 
            _status = Status.Open;
 
            _lastColumnRead = -1; 
            _lastDataOffsetRead = -1;
            _lastOrdinalCheckedForNull = -1; 
            _lastValueCheckedForNull = null;
        }

        #endregion 

        #region assertion helpers 
 
        /// 
        /// Ensures that the reader is actually open, and throws an exception if not 
        /// 
        private void AssertReaderIsOpen() {
            if (IsExplicitlyClosed) {
                throw EntityUtil.ClosedDataReaderError(); 
            }
            if (IsImplicitlyClosed) { 
                throw EntityUtil.ImplicitlyClosedDataReaderError(); 
            }
        } 

        /// 
        /// Helper method.
        ///  
        private void AssertReaderIsOpenWithData() {
            AssertReaderIsOpen(); 
 
            if (!HasData) {
                throw EntityUtil.NoData(); 
            }
        }

        ///  
        /// Ensures that sequential access rules are being obeyed for non-chunking
        /// getter methods, throws the appropriate exception if not.  Also ensures 
        /// that the last column/chunk is set appropriately. 
        /// 
        ///  
        private void AssertSequentialAccess(int ordinal) {
            Debug.Assert(null != _source, "null _source?"); // we should have already called AssertReaderIsOpen.

            if (ordinal < 0 || ordinal >= _source.ColumnCount) { 
                throw EntityUtil.ArgumentOutOfRange("ordinal");
            } 
            if (_lastColumnRead >= ordinal) { 
                throw EntityUtil.NonSequentialColumnAccess(ordinal, _lastColumnRead + 1);
            } 
            _lastColumnRead = ordinal;
            // SQLBUDT #442001 -- we need to mark things that are not using GetBytes/GetChars
            //                    in a way that prevents them from being read a second time
            //                    using those methods.  Pointing past any potential data is 
            //                    how we do that.
            _lastDataOffsetRead = long.MaxValue; 
        } 

        ///  
        /// Ensures that sequential access rules are being obeyed for chunking
        /// getter methods, throws the appropriate exception if not.  Also ensures
        /// that the last column/chunk is set appropriately.
        ///  
        /// 
        ///  
        ///  
        private void AssertSequentialAccess(int ordinal, long dataOffset, string methodName) {
            Debug.Assert(null != _source, "null _source?"); // we should have already called AssertReaderIsOpen. 

            if (ordinal < 0 || ordinal >= _source.ColumnCount) {
                throw EntityUtil.ArgumentOutOfRange("ordinal");
            } 
            if (_lastColumnRead > ordinal || (_lastColumnRead == ordinal && _lastDataOffsetRead == long.MaxValue)) {
                throw EntityUtil.NonSequentialColumnAccess(ordinal, _lastColumnRead + 1); 
            } 
            if (_lastColumnRead == ordinal) {
                if (_lastDataOffsetRead >= dataOffset) { 
                    throw EntityUtil.NonSequentialChunkAccess(dataOffset, _lastDataOffsetRead + 1, methodName);
                }
                // _lastDataOffsetRead will be set by GetBytes/GetChars, since we need to set it
                // to the last offset that was actually read, which isn't necessarily what was 
                // requested.
            } 
            else { 
                // Doin' a new thang...
                _lastColumnRead = ordinal; 
                _lastDataOffsetRead = -1;
            }
        }
 
        /// 
        /// True when the record has data (SetRecordSource was called with true) 
        ///  
        internal bool HasData {
            get { 
                bool result = (_source != null);
                return result;
            }
        } 

        ///  
        /// True so long as we haven't been closed either implicity or explictly 
        /// 
        internal bool IsClosed { 
            get {
                return (_status != Status.Open);
            }
        } 

        ///  
        /// Determine whether we have been explicitly closed by our owning 
        /// data reader; only data records that are responsible for processing
        /// data reader requests can be explicitly closed; 
        /// 
        internal bool IsExplicitlyClosed {
            get {
                return (_status == Status.ClosedExplicitly); 
            }
        } 
 
        /// 
        /// Determine whether the parent data reader or record moved on from 
        /// where we can be considered open, (because the consumer of the
        /// parent data reader/record called either the GetValue() or Read()
        /// methods on the parent);
        ///  
        internal bool IsImplicitlyClosed {
            get { 
                return (_status == Status.ClosedImplicitly); 
            }
        } 
        #endregion

        #region metadata properties and methods
 
        /// 
        /// implementation of DbDataRecord.DataRecordInfo property 
        ///  
        public DataRecordInfo DataRecordInfo {
            get { 
                AssertReaderIsOpen();
                DataRecordInfo result = _source.DataRecordInfo;
                return result;
            } 
        }
 
        ///  
        /// implementation of DbDataRecord.FieldCount property
        ///  
        override public int FieldCount {
            get {
                AssertReaderIsOpen();
                return _source.ColumnCount; 
            }
        } 
 
        /// 
        /// Helper method to get the edm TypeUsage for the specified column; 
        ///
        /// If the column requested is a record, we'll pick up whatever the
        /// current record says it is, otherwise we'll take whatever was stored
        /// on our record state. 
        /// 
        ///  
        ///  
        private TypeUsage GetTypeUsage(int ordinal) {
            // Some folks are picky about the exception we throw 
            if (ordinal < 0 || ordinal >= _source.ColumnCount) {
                throw EntityUtil.ArgumentOutOfRange("ordinal");
            }
            TypeUsage result; 

            // 
            RecordState recordState = _source.CurrentColumnValues[ordinal] as RecordState; 
            if (null != recordState) {
                result = recordState.DataRecordInfo.RecordType; 
            }
            else {
                result = _source.GetTypeUsage(ordinal);
            } 
            return result;
        } 
 
        /// 
        /// implementation of DbDataRecord.GetDataTypeName() method 
        /// 
        /// 
        /// 
        override public string GetDataTypeName(int ordinal) { 
            AssertReaderIsOpenWithData();
            return TypeHelpers.GetFullName(GetTypeUsage(ordinal)); 
        } 

        ///  
        /// implementation of DbDataRecord.GetFieldType() method
        /// 
        /// 
        ///  
        override public Type GetFieldType(int ordinal) {
            AssertReaderIsOpenWithData(); 
            return BridgeDataReader.GetClrTypeFromTypeMetadata(GetTypeUsage(ordinal)); 
        }
 
        /// 
        /// implementation of DbDataRecord.GetName() method
        /// 
        ///  
        /// 
        override public string GetName(int ordinal) { 
            AssertReaderIsOpen(); 
            return _source.GetName(ordinal);
        } 

        /// 
        /// implementation of DbDataRecord.GetOrdinal() method
        ///  
        /// 
        ///  
        override public int GetOrdinal(string name) { 
            AssertReaderIsOpen();
            return _source.GetOrdinal(name); 
        }

        #endregion
 
        #region general getter methods and indexer properties
 
        ///  
        /// implementation for DbDataRecord[ordinal] indexer property
        ///  
        /// 
        /// 
        override public object this[int ordinal] {
            get { 
                return GetValue(ordinal);
            } 
        } 

        ///  
        /// implementation for DbDataRecord[name] indexer property
        /// 
        /// 
        ///  
        override public object this[string name] {
            get { 
                return GetValue(GetOrdinal(name)); 
            }
        } 

        /// 
        /// implementation for DbDataRecord.GetValue() method
        /// 
        /// This method is used by most of the column getters on this
        /// class to retrieve the value from the source reader.  Therefore, 
        /// it asserts all the good things, like that the reader is open, 
        /// and that it has data, and that you're not trying to circumvent
        /// sequential access requirements. 
        /// 
        /// 
        /// 
        override public Object GetValue(int ordinal) { 
            AssertReaderIsOpenWithData();
            AssertSequentialAccess(ordinal); 
 
            object result = null;
 
            if (ordinal == _lastOrdinalCheckedForNull) {
                result = _lastValueCheckedForNull;
            }
            else { 
                _lastOrdinalCheckedForNull = -1;
                _lastValueCheckedForNull = null; 
 
                CloseNestedObjectImplicitly();
 
                result = _source.CurrentColumnValues[ordinal];

                // If we've got something that's nested, then make sure we
                // update the current nested record with it so we can be certain 
                // to close it implicitly when we move past it.
                if (_source.IsNestedObject(ordinal)) { 
                    result = GetNestedObjectValue(result); 
                }
            } 
            return result;
        }

        ///  
        /// For nested objects (records/readers) we have a bit more work to do; this
        /// method extracts it all out from the main GetValue method so it doesn't 
        /// have to be so big. 
        /// 
        ///  
        /// 
        private object GetNestedObjectValue(object result) {
            if (result != DBNull.Value) {
                RecordState recordState = result as RecordState; 
                if (null != recordState) {
                    if (recordState.IsNull) { 
                        result = DBNull.Value; 
                    }
                    else { 
                        BridgeDataRecord nestedRecord = new BridgeDataRecord(Shaper, Depth + 1);
                        nestedRecord.SetRecordSource(recordState, true);
                        result = nestedRecord;
                        _currentNestedRecord = nestedRecord; 
                        _currentNestedReader = null;
                    } 
                } 
                else {
                    Coordinator coordinator = result as Coordinator; 
                    if (null != coordinator) {
                        BridgeDataReader nestedReader = new BridgeDataReader(Shaper, coordinator.TypedCoordinatorFactory, Depth + 1);
                        result = nestedReader;
                        _currentNestedRecord = null; 
                        _currentNestedReader = nestedReader;
                    } 
                    else { 
                        Debug.Fail("unexpected type of nested object result: " + result.GetType().ToString());
                    } 
                }
            }
            return result;
        } 

        ///  
        /// implementation for DbDataRecord.GetValues() method 
        /// 
        ///  
        /// 
        override public int GetValues(object[] values) {
            EntityUtil.CheckArgumentNull(values, "values");
 
            int copy = Math.Min(values.Length, FieldCount);
            for (int i = 0; i < copy; ++i) { 
                values[i] = GetValue(i); 
            }
            return copy; 
        }

        #endregion
 
        #region simple scalar value getter methods
 
        ///  
        /// implementation of DbDataRecord.GetBoolean() method
        ///  
        /// 
        /// 
        override public bool GetBoolean(int ordinal) {
            return (bool)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetByte() method
        ///  
        /// 
        /// 
        override public byte GetByte(int ordinal) {
            return (byte)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetChar() method
        ///  
        /// 
        /// 
        override public char GetChar(int ordinal) {
            return (char)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetDateTime() method
        ///  
        /// 
        /// 
        override public DateTime GetDateTime(int ordinal) {
            return (DateTime)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetDecimal() method
        ///  
        /// 
        /// 
        override public Decimal GetDecimal(int ordinal) {
            return (Decimal)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetDouble() method
        ///  
        /// 
        /// 
        override public double GetDouble(int ordinal) {
            return (double)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetFloat() method
        ///  
        /// 
        /// 
        override public float GetFloat(int ordinal) {
            return (float)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetGuid() method
        ///  
        /// 
        /// 
        override public Guid GetGuid(int ordinal) {
            return (Guid)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetInt16() method
        ///  
        /// 
        /// 
        override public Int16 GetInt16(int ordinal) {
            return (Int16)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetInt32() method
        ///  
        /// 
        /// 
        override public Int32 GetInt32(int ordinal) {
            return (Int32)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetInt64() method
        ///  
        /// 
        /// 
        override public Int64 GetInt64(int ordinal) {
            return (Int64)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.GetString() method
        ///  
        /// 
        /// 
        override public String GetString(int ordinal) {
            return (String)GetValue(ordinal); 
        }
 
        ///  
        /// implementation of DbDataRecord.IsDBNull() method
        ///  
        /// 
        /// 
        override public bool IsDBNull(int ordinal) {
            // This seems like a hack, but the the problem is that I need 
            // to make sure I don't monkey with caching things, and if I
            // call IsDBNull directly on the store reader, I'll potentially 
            // lose data because I'm expecting SequentialAccess rules. 

            object columnValue = GetValue(ordinal); 

            // Need to backup one because we technically didn't read the
            // value yet but the GetValue method advanced our pointer to
            // what the value was.  Another hack, but it's way less code 
            // than trying to avoid advancing to begin with.
            _lastColumnRead--; 
            _lastDataOffsetRead = -1; 

            // So as to avoid reconstructing nested records, readers, and 
            // rereading data from the iterator source cache, we just cache
            // the value we read and the ordinal it came from, so if someone
            // is doing the right thing(TM) and calling IsDBNull before calling
            // GetValue, we won't construct another one. 
            _lastValueCheckedForNull = columnValue;
            _lastOrdinalCheckedForNull = ordinal; 
 
            bool result = (DBNull.Value == columnValue);
 
            return result;
        }

        #endregion 

        #region chunking scalar value getter methods 
 
        /// 
        /// implementation for DbDataRecord.GetBytes() method 
        /// 
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        override public long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) {
            AssertReaderIsOpenWithData(); 
            AssertSequentialAccess(ordinal, dataOffset, "GetBytes");

            long result = _source.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
 
            if (buffer != null) {
                _lastDataOffsetRead = dataOffset + result - 1; // just what was read, nothing more. 
            } 
            return result;
        } 

        /// 
        /// implementation for DbDataRecord.GetChars() method
        ///  
        /// 
        ///  
        ///  
        /// 
        ///  
        /// 
        override public long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) {
            AssertReaderIsOpenWithData();
            AssertSequentialAccess(ordinal, dataOffset, "GetChars"); 

            long result = _source.GetChars(ordinal, dataOffset, buffer, bufferOffset, length); 
 
            if (buffer != null) {
                _lastDataOffsetRead = dataOffset + result - 1; // just what was read, nothing more. 
            }
            return result;
        }
 
        #endregion
 
        #region complex type getters 

        ///  
        /// implementation for DbDataRecord.GetData() method
        /// 
        /// 
        ///  
        override protected DbDataReader GetDbDataReader(int ordinal) {
            return (DbDataReader)GetValue(ordinal); 
        } 

        ///  
        /// implementation for DbDataRecord.GetDataRecord() method
        /// 
        /// 
        ///  
        public DbDataRecord GetDataRecord(int ordinal) {
            return (DbDataRecord)GetValue(ordinal); 
        } 

        ///  
        /// Used to return a nested result
        /// 
        /// 
        ///  
        public DbDataReader GetDataReader(int ordinal) {
            return this.GetDbDataReader(ordinal); 
        } 

        #endregion 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK