BridgeDataRecord.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Query / ResultAssembly / BridgeDataRecord.cs / 4 / BridgeDataRecord.cs

                            //------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// @owner  [....], [....] 
//---------------------------------------------------------------------
 
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  [....], [....] 
//---------------------------------------------------------------------
 
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