@@ -87,6 +87,7 @@ internal class SharedState
8787
8888        private  Task  _currentTask ; 
8989        private  Snapshot  _snapshot ; 
90+         private  Snapshot  _cachedSnapshot ; 
9091        private  CancellationTokenSource  _cancelAsyncOnCloseTokenSource ; 
9192        private  CancellationToken  _cancelAsyncOnCloseToken ; 
9293
@@ -803,7 +804,7 @@ private bool TryCleanPartialRead()
803804            } 
804805
805806#if DEBUG 
806-             if  ( _stateObj . _pendingData ) 
807+             if  ( _stateObj . HasPendingData ) 
807808            { 
808809                byte  token ; 
809810                if  ( ! _stateObj . TryPeekByte ( out  token ) ) 
@@ -936,7 +937,7 @@ private bool TryCloseInternal(bool closeReader)
936937
937938            try 
938939            { 
939-                 if  ( ( ! _isClosed )  &&  ( parser  !=  null )  &&  ( stateObj  !=  null )  &&  ( stateObj . _pendingData ) ) 
940+                 if  ( ( ! _isClosed )  &&  ( parser  !=  null )  &&  ( stateObj  !=  null )  &&  ( stateObj . HasPendingData ) ) 
940941                { 
941942                    // It is possible for this to be called during connection close on a 
942943                    // broken connection, so check state first. 
@@ -1118,7 +1119,7 @@ private bool TryConsumeMetaData()
11181119        { 
11191120            // warning:  Don't check the MetaData property within this function 
11201121            // warning:  as it will be a reentrant call 
1121-             while  ( _parser  !=  null  &&  _stateObj  !=  null  &&  _stateObj . _pendingData  &&  ! _metaDataConsumed ) 
1122+             while  ( _parser  !=  null  &&  _stateObj  !=  null  &&  _stateObj . HasPendingData  &&  ! _metaDataConsumed ) 
11221123            { 
11231124                if  ( _parser . State  ==  TdsParserState . Broken  ||  _parser . State  ==  TdsParserState . Closed ) 
11241125                { 
@@ -3053,7 +3054,7 @@ private bool TryHasMoreResults(out bool moreResults)
30533054
30543055                Debug . Assert ( null  !=  _command ,  "unexpected null command from the data reader!" ) ; 
30553056
3056-                 while  ( _stateObj . _pendingData ) 
3057+                 while  ( _stateObj . HasPendingData ) 
30573058                { 
30583059                    byte  token ; 
30593060                    if  ( ! _stateObj . TryPeekByte ( out  token ) ) 
@@ -3137,7 +3138,7 @@ private bool TryHasMoreRows(out bool moreRows)
31373138                        moreRows  =  false ; 
31383139                        return  true ; 
31393140                } 
3140-                 if  ( _stateObj . _pendingData ) 
3141+                 if  ( _stateObj . HasPendingData ) 
31413142                { 
31423143                    // Consume error's, info's, done's on HasMoreRows, so user obtains error on Read. 
31433144                    byte  b ; 
@@ -3178,7 +3179,7 @@ private bool TryHasMoreRows(out bool moreRows)
31783179                            moreRows  =  false ; 
31793180                            return  false ; 
31803181                        } 
3181-                         if  ( _stateObj . _pendingData ) 
3182+                         if  ( _stateObj . HasPendingData ) 
31823183                        { 
31833184                            if  ( ! _stateObj . TryPeekByte ( out  b ) ) 
31843185                            { 
@@ -3451,7 +3452,7 @@ private bool TryReadInternal(bool setTimeout, out bool more)
34513452                        if  ( moreRows ) 
34523453                        { 
34533454                            // read the row from the backend (unless it's an altrow were the marker is already inside the altrow ...) 
3454-                             while  ( _stateObj . _pendingData ) 
3455+                             while  ( _stateObj . HasPendingData ) 
34553456                            { 
34563457                                if  ( _altRowStatus  !=  ALTROWSTATUS . AltRow ) 
34573458                                { 
@@ -3483,7 +3484,7 @@ private bool TryReadInternal(bool setTimeout, out bool more)
34833484                            } 
34843485                        } 
34853486
3486-                         if  ( ! _stateObj . _pendingData ) 
3487+                         if  ( ! _stateObj . HasPendingData ) 
34873488                        { 
34883489                            if  ( ! TryCloseInternal ( false  /*closeReader*/ ) ) 
34893490                            { 
@@ -3507,7 +3508,7 @@ private bool TryReadInternal(bool setTimeout, out bool more)
35073508                        { 
35083509                            // if we are in SingleRow mode, and we've read the first row, 
35093510                            // read the rest of the rows, if any 
3510-                             while  ( _stateObj . _pendingData  &&  ! _sharedState . _dataReady ) 
3511+                             while  ( _stateObj . HasPendingData  &&  ! _sharedState . _dataReady ) 
35113512                            { 
35123513                                if  ( ! _parser . TryRun ( RunBehavior . ReturnImmediately ,  _command ,  this ,  null ,  _stateObj ,  out  _sharedState . _dataReady ) ) 
35133514                                { 
@@ -3548,7 +3549,7 @@ private bool TryReadInternal(bool setTimeout, out bool more)
35483549                more  =  false ; 
35493550
35503551#if DEBUG 
3551-                 if  ( ( ! _sharedState . _dataReady )  &&  ( _stateObj . _pendingData ) ) 
3552+                 if  ( ( ! _sharedState . _dataReady )  &&  ( _stateObj . HasPendingData ) ) 
35523553                { 
35533554                    byte  token ; 
35543555                    if  ( ! _stateObj . TryPeekByte ( out  token ) ) 
@@ -3770,6 +3771,10 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false)
37703771                { 
37713772                    // reset snapshot to save memory use.  We can safely do that here because all SqlDataReader values are stable. 
37723773                    // The retry logic can use the current values to get back to the right state. 
3774+                     if  ( _cachedSnapshot  is  null ) 
3775+                     { 
3776+                         _cachedSnapshot  =  _snapshot ; 
3777+                     } 
37733778                    _snapshot  =  null ; 
37743779                    PrepareAsyncInvocation ( useSnapshot :  true ) ; 
37753780                } 
@@ -4659,6 +4664,10 @@ public override Task<bool> ReadAsync(CancellationToken cancellationToken)
46594664                        if  ( ! rowTokenRead ) 
46604665                        { 
46614666                            rowTokenRead  =  true ; 
4667+                             if  ( _cachedSnapshot  is  null ) 
4668+                             { 
4669+                                 _cachedSnapshot  =  _snapshot ; 
4670+                             } 
46624671                            _snapshot  =  null ; 
46634672                            PrepareAsyncInvocation ( useSnapshot :  true ) ; 
46644673                        } 
@@ -5112,28 +5121,27 @@ private void PrepareAsyncInvocation(bool useSnapshot)
51125121
51135122                if  ( _snapshot  ==  null ) 
51145123                { 
5115-                     _snapshot  =  new  Snapshot 
5116-                     { 
5117-                         _dataReady  =  _sharedState . _dataReady , 
5118-                         _haltRead  =  _haltRead , 
5119-                         _metaDataConsumed  =  _metaDataConsumed , 
5120-                         _browseModeInfoConsumed  =  _browseModeInfoConsumed , 
5121-                         _hasRows  =  _hasRows , 
5122-                         _altRowStatus  =  _altRowStatus , 
5123-                         _nextColumnDataToRead  =  _sharedState . _nextColumnDataToRead , 
5124-                         _nextColumnHeaderToRead  =  _sharedState . _nextColumnHeaderToRead , 
5125-                         _columnDataBytesRead  =  _columnDataBytesRead , 
5126-                         _columnDataBytesRemaining  =  _sharedState . _columnDataBytesRemaining , 
5127- 
5128-                         // _metadata and _altaMetaDataSetCollection must be Cloned 
5129-                         // before they are updated 
5130-                         _metadata  =  _metaData , 
5131-                         _altMetaDataSetCollection  =  _altMetaDataSetCollection , 
5132-                         _tableNames  =  _tableNames , 
5133- 
5134-                         _currentStream  =  _currentStream , 
5135-                         _currentTextReader  =  _currentTextReader , 
5136-                     } ; 
5124+                     _snapshot  =  Interlocked . Exchange ( ref  _cachedSnapshot ,  null )  ??  new  Snapshot ( ) ; 
5125+ 
5126+                     _snapshot . _dataReady  =  _sharedState . _dataReady ; 
5127+                     _snapshot . _haltRead  =  _haltRead ; 
5128+                     _snapshot . _metaDataConsumed  =  _metaDataConsumed ; 
5129+                     _snapshot . _browseModeInfoConsumed  =  _browseModeInfoConsumed ; 
5130+                     _snapshot . _hasRows  =  _hasRows ; 
5131+                     _snapshot . _altRowStatus  =  _altRowStatus ; 
5132+                     _snapshot . _nextColumnDataToRead  =  _sharedState . _nextColumnDataToRead ; 
5133+                     _snapshot . _nextColumnHeaderToRead  =  _sharedState . _nextColumnHeaderToRead ; 
5134+                     _snapshot . _columnDataBytesRead  =  _columnDataBytesRead ; 
5135+                     _snapshot . _columnDataBytesRemaining  =  _sharedState . _columnDataBytesRemaining ; 
5136+ 
5137+                     // _metadata and _altaMetaDataSetCollection must be Cloned 
5138+                     // before they are updated 
5139+                     _snapshot . _metadata  =  _metaData ; 
5140+                     _snapshot . _altMetaDataSetCollection  =  _altMetaDataSetCollection ; 
5141+                     _snapshot . _tableNames  =  _tableNames ; 
5142+ 
5143+                     _snapshot . _currentStream  =  _currentStream ; 
5144+                     _snapshot . _currentTextReader  =  _currentTextReader ; 
51375145
51385146                    _stateObj . SetSnapshot ( ) ; 
51395147                } 
@@ -5186,6 +5194,10 @@ private void CleanupAfterAsyncInvocationInternal(TdsParserStateObject stateObj,
51865194            stateObj . _permitReplayStackTraceToDiffer  =  false ; 
51875195#endif
51885196
5197+             if  ( _cachedSnapshot  is  null ) 
5198+             { 
5199+                 _cachedSnapshot  =  _snapshot ; 
5200+             } 
51895201            // We are setting this to null inside the if-statement because stateObj==null means that the reader hasn't been initialized or has been closed (either way _snapshot should already be null) 
51905202            _snapshot  =  null ; 
51915203        } 
@@ -5224,6 +5236,10 @@ private void SwitchToAsyncWithoutSnapshot()
52245236            Debug . Assert ( _snapshot  !=  null ,  "Should currently have a snapshot" ) ; 
52255237            Debug . Assert ( _stateObj  !=  null  &&  ! _stateObj . _asyncReadWithoutSnapshot ,  "Already in async without snapshot" ) ; 
52265238
5239+             if  ( _cachedSnapshot  is  null ) 
5240+             { 
5241+                 _cachedSnapshot  =  _snapshot ; 
5242+             } 
52275243            _snapshot  =  null ; 
52285244            _stateObj . ResetSnapshot ( ) ; 
52295245            _stateObj . _asyncReadWithoutSnapshot  =  true ; 
0 commit comments