@@ -30,7 +30,7 @@ namespace Microsoft.Data.SqlClient
30
30
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/SqlDataReader/*' />
31
31
public class SqlDataReader : DbDataReader , IDataReader , IDbColumnSchemaGenerator
32
32
{
33
- private enum ALTROWSTATUS
33
+ internal enum ALTROWSTATUS
34
34
{
35
35
Null = 0 , // default and after Done
36
36
AltRow , // after calling NextResult and the first AltRow is available for read
@@ -97,9 +97,6 @@ internal class SharedState
97
97
private SqlSequentialStream _currentStream ;
98
98
private SqlSequentialTextReader _currentTextReader ;
99
99
100
- private IsDBNullAsyncCallContext _cachedIsDBNullContext ;
101
- private ReadAsyncCallContext _cachedReadAsyncContext ;
102
-
103
100
internal SqlDataReader ( SqlCommand command , CommandBehavior behavior )
104
101
{
105
102
SqlConnection . VerifyExecutePermission ( ) ;
@@ -4387,6 +4384,10 @@ internal TdsOperationStatus TryReadColumnInternal(int i, bool readHeaderOnly/* =
4387
4384
{
4388
4385
// reset snapshot to save memory use. We can safely do that here because all SqlDataReader values are stable.
4389
4386
// The retry logic can use the current values to get back to the right state.
4387
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection && sqlInternalConnection . CachedDataReaderSnapshot is null )
4388
+ {
4389
+ sqlInternalConnection . CachedDataReaderSnapshot = _snapshot ;
4390
+ }
4390
4391
_snapshot = null ;
4391
4392
PrepareAsyncInvocation ( useSnapshot : true ) ;
4392
4393
}
@@ -5318,7 +5319,15 @@ public override Task<bool> ReadAsync(CancellationToken cancellationToken)
5318
5319
return source . Task ;
5319
5320
}
5320
5321
5321
- var context = Interlocked . Exchange ( ref _cachedReadAsyncContext , null ) ?? new ReadAsyncCallContext ( ) ;
5322
+ ReadAsyncCallContext context = null ;
5323
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5324
+ {
5325
+ context = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderReadAsyncContext , null ) ;
5326
+ }
5327
+ if ( context is null )
5328
+ {
5329
+ context = new ReadAsyncCallContext ( ) ;
5330
+ }
5322
5331
5323
5332
Debug . Assert ( context . Reader == null && context . Source == null && context . Disposable == default , "cached ReadAsyncCallContext was not properly disposed" ) ;
5324
5333
@@ -5358,6 +5367,10 @@ private static Task<bool> ReadAsyncExecute(Task task, object state)
5358
5367
if ( ! hasReadRowToken )
5359
5368
{
5360
5369
hasReadRowToken = true ;
5370
+ if ( reader . Connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection && sqlInternalConnection . CachedDataReaderSnapshot is null )
5371
+ {
5372
+ sqlInternalConnection . CachedDataReaderSnapshot = reader . _snapshot ;
5373
+ }
5361
5374
reader . _snapshot = null ;
5362
5375
reader . PrepareAsyncInvocation ( useSnapshot : true ) ;
5363
5376
}
@@ -5377,7 +5390,10 @@ private static Task<bool> ReadAsyncExecute(Task task, object state)
5377
5390
5378
5391
private void SetCachedReadAsyncCallContext ( ReadAsyncCallContext instance )
5379
5392
{
5380
- Interlocked . CompareExchange ( ref _cachedReadAsyncContext , instance , null ) ;
5393
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5394
+ {
5395
+ Interlocked . CompareExchange ( ref sqlInternalConnection . CachedDataReaderReadAsyncContext , instance , null ) ;
5396
+ }
5381
5397
}
5382
5398
5383
5399
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/IsDBNullAsync/*' />
@@ -5479,7 +5495,15 @@ override public Task<bool> IsDBNullAsync(int i, CancellationToken cancellationTo
5479
5495
registrationHolder . Set ( cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ) ;
5480
5496
}
5481
5497
5482
- IsDBNullAsyncCallContext context = Interlocked . Exchange ( ref _cachedIsDBNullContext , null ) ?? new IsDBNullAsyncCallContext ( ) ;
5498
+ IsDBNullAsyncCallContext context = null ;
5499
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5500
+ {
5501
+ context = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderIsDBNullContext , null ) ;
5502
+ }
5503
+ if ( context is null )
5504
+ {
5505
+ context = new IsDBNullAsyncCallContext ( ) ;
5506
+ }
5483
5507
5484
5508
Debug . Assert ( context . Reader == null && context . Source == null && context . Disposable == default , "cached ISDBNullAsync context not properly disposed" ) ;
5485
5509
@@ -5517,7 +5541,10 @@ private static Task<bool> IsDBNullAsyncExecute(Task task, object state)
5517
5541
5518
5542
private void SetCachedIDBNullAsyncCallContext ( IsDBNullAsyncCallContext instance )
5519
5543
{
5520
- Interlocked . CompareExchange ( ref _cachedIsDBNullContext , instance , null ) ;
5544
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5545
+ {
5546
+ Interlocked . CompareExchange ( ref sqlInternalConnection . CachedDataReaderIsDBNullContext , instance , null ) ;
5547
+ }
5521
5548
}
5522
5549
5523
5550
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/GetFieldValueAsync/*' />
@@ -6019,7 +6046,7 @@ private void CompleteAsyncCall<T>(Task<T> task, SqlDataReaderBaseAsyncCallContex
6019
6046
}
6020
6047
}
6021
6048
6022
- private sealed class Snapshot
6049
+ internal sealed class Snapshot
6023
6050
{
6024
6051
public bool _dataReady ;
6025
6052
public bool _haltRead ;
@@ -6051,7 +6078,14 @@ private void PrepareAsyncInvocation(bool useSnapshot)
6051
6078
6052
6079
if ( _snapshot == null )
6053
6080
{
6054
- _snapshot = new Snapshot ( ) ;
6081
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
6082
+ {
6083
+ _snapshot = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderSnapshot , null ) ?? new Snapshot ( ) ;
6084
+ }
6085
+ else
6086
+ {
6087
+ _snapshot = new Snapshot ( ) ;
6088
+ }
6055
6089
6056
6090
_snapshot . _dataReady = _sharedState . _dataReady ;
6057
6091
_snapshot . _haltRead = _haltRead ;
@@ -6124,6 +6158,10 @@ private void CleanupAfterAsyncInvocationInternal(TdsParserStateObject stateObj,
6124
6158
stateObj . _permitReplayStackTraceToDiffer = false ;
6125
6159
#endif
6126
6160
6161
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection && sqlInternalConnection . CachedDataReaderSnapshot is null )
6162
+ {
6163
+ sqlInternalConnection . CachedDataReaderSnapshot = _snapshot ;
6164
+ }
6127
6165
// 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)
6128
6166
_snapshot = null ;
6129
6167
}
@@ -6162,6 +6200,10 @@ private void SwitchToAsyncWithoutSnapshot()
6162
6200
Debug . Assert ( _snapshot != null , "Should currently have a snapshot" ) ;
6163
6201
Debug . Assert ( _stateObj != null && ! _stateObj . _asyncReadWithoutSnapshot , "Already in async without snapshot" ) ;
6164
6202
6203
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection && sqlInternalConnection . CachedDataReaderSnapshot is null )
6204
+ {
6205
+ sqlInternalConnection . CachedDataReaderSnapshot = _snapshot ;
6206
+ }
6165
6207
_snapshot = null ;
6166
6208
_stateObj . ResetSnapshot ( ) ;
6167
6209
_stateObj . _asyncReadWithoutSnapshot = true ;
0 commit comments