@@ -27,7 +27,7 @@ namespace Microsoft.Data.SqlClient
27
27
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/SqlDataReader/*' />
28
28
public class SqlDataReader : DbDataReader , IDataReader , IDbColumnSchemaGenerator
29
29
{
30
- private enum ALTROWSTATUS
30
+ internal enum ALTROWSTATUS
31
31
{
32
32
Null = 0 , // default and after Done
33
33
AltRow , // after calling NextResult and the first AltRow is available for read
@@ -87,7 +87,7 @@ internal class SharedState
87
87
88
88
private Task _currentTask ;
89
89
private Snapshot _snapshot ;
90
- private Snapshot _cachedSnapshot ;
90
+
91
91
private CancellationTokenSource _cancelAsyncOnCloseTokenSource ;
92
92
private CancellationToken _cancelAsyncOnCloseToken ;
93
93
@@ -97,8 +97,6 @@ internal class SharedState
97
97
98
98
private SqlSequentialStream _currentStream ;
99
99
private SqlSequentialTextReader _currentTextReader ;
100
- private IsDBNullAsyncCallContext _cachedIsDBNullContext ;
101
- private ReadAsyncCallContext _cachedReadAsyncContext ;
102
100
103
101
internal SqlDataReader ( SqlCommand command , CommandBehavior behavior )
104
102
{
@@ -3781,9 +3779,9 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false)
3781
3779
{
3782
3780
// reset snapshot to save memory use. We can safely do that here because all SqlDataReader values are stable.
3783
3781
// The retry logic can use the current values to get back to the right state.
3784
- if ( _cachedSnapshot is null )
3782
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection && sqlInternalConnection . CachedDataReaderSnapshot is null )
3785
3783
{
3786
- _cachedSnapshot = _snapshot ;
3784
+ sqlInternalConnection . CachedDataReaderSnapshot = _snapshot ;
3787
3785
}
3788
3786
_snapshot = null ;
3789
3787
PrepareAsyncInvocation ( useSnapshot : true ) ;
@@ -4705,7 +4703,15 @@ public override Task<bool> ReadAsync(CancellationToken cancellationToken)
4705
4703
registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
4706
4704
}
4707
4705
4708
- var context = Interlocked . Exchange ( ref _cachedReadAsyncContext , null ) ?? new ReadAsyncCallContext ( ) ;
4706
+ ReadAsyncCallContext context = null ;
4707
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
4708
+ {
4709
+ context = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderReadAsyncContext , null ) ;
4710
+ }
4711
+ if ( context is null )
4712
+ {
4713
+ context = new ReadAsyncCallContext ( ) ;
4714
+ }
4709
4715
4710
4716
Debug . Assert ( context . _reader == null && context . _source == null && context . _disposable == null , "cached ReadAsyncCallContext was not properly disposed" ) ;
4711
4717
@@ -4749,9 +4755,9 @@ private static Task<bool> ReadAsyncExecute(Task task, object state)
4749
4755
if ( ! hasReadRowToken )
4750
4756
{
4751
4757
hasReadRowToken = true ;
4752
- if ( reader . _cachedSnapshot is null )
4758
+ if ( reader . Connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection && sqlInternalConnection . CachedDataReaderSnapshot is null )
4753
4759
{
4754
- reader . _cachedSnapshot = reader . _snapshot ;
4760
+ sqlInternalConnection . CachedDataReaderSnapshot = reader . _snapshot ;
4755
4761
}
4756
4762
reader . _snapshot = null ;
4757
4763
reader . PrepareAsyncInvocation ( useSnapshot : true ) ;
@@ -4771,7 +4777,10 @@ private static Task<bool> ReadAsyncExecute(Task task, object state)
4771
4777
4772
4778
private void SetCachedReadAsyncCallContext ( ReadAsyncCallContext instance )
4773
4779
{
4774
- Interlocked . CompareExchange ( ref _cachedReadAsyncContext , instance , null ) ;
4780
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
4781
+ {
4782
+ Interlocked . CompareExchange ( ref sqlInternalConnection . CachedDataReaderReadAsyncContext , instance , null ) ;
4783
+ }
4775
4784
}
4776
4785
4777
4786
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/IsDBNullAsync/*' />
@@ -4872,7 +4881,15 @@ override public Task<bool> IsDBNullAsync(int i, CancellationToken cancellationTo
4872
4881
registration = cancellationToken . Register ( SqlCommand . s_cancelIgnoreFailure , _command ) ;
4873
4882
}
4874
4883
4875
- IsDBNullAsyncCallContext context = Interlocked . Exchange ( ref _cachedIsDBNullContext , null ) ?? new IsDBNullAsyncCallContext ( ) ;
4884
+ IsDBNullAsyncCallContext context = null ;
4885
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
4886
+ {
4887
+ context = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderIsDBNullContext , null ) ;
4888
+ }
4889
+ if ( context is null )
4890
+ {
4891
+ context = new IsDBNullAsyncCallContext ( ) ;
4892
+ }
4876
4893
4877
4894
Debug . Assert ( context . _reader == null && context . _source == null && context . _disposable == null , "cached ISDBNullAsync context not properly disposed" ) ;
4878
4895
@@ -4908,7 +4925,10 @@ private static Task<bool> IsDBNullAsyncExecute(Task task, object state)
4908
4925
4909
4926
private void SetCachedIDBNullAsyncCallContext ( IsDBNullAsyncCallContext instance )
4910
4927
{
4911
- Interlocked . CompareExchange ( ref _cachedIsDBNullContext , instance , null ) ;
4928
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
4929
+ {
4930
+ Interlocked . CompareExchange ( ref sqlInternalConnection . CachedDataReaderIsDBNullContext , instance , null ) ;
4931
+ }
4912
4932
}
4913
4933
4914
4934
/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/GetFieldValueAsync/*' />
@@ -5056,7 +5076,7 @@ internal void CompletePendingReadWithFailure(int errorCode, bool resetForcePendi
5056
5076
5057
5077
#endif
5058
5078
5059
- private class Snapshot
5079
+ internal class Snapshot
5060
5080
{
5061
5081
public bool _dataReady ;
5062
5082
public bool _haltRead ;
@@ -5077,7 +5097,7 @@ private class Snapshot
5077
5097
public SqlSequentialTextReader _currentTextReader ;
5078
5098
}
5079
5099
5080
- private abstract class AAsyncCallContext < T > : IDisposable
5100
+ internal abstract class AAsyncCallContext < T > : IDisposable
5081
5101
{
5082
5102
internal static readonly Action < Task < T > , object > s_completeCallback = SqlDataReader . CompleteAsyncCallCallback < T > ;
5083
5103
@@ -5120,7 +5140,7 @@ public virtual void Dispose()
5120
5140
}
5121
5141
}
5122
5142
5123
- private sealed class ReadAsyncCallContext : AAsyncCallContext < bool >
5143
+ internal sealed class ReadAsyncCallContext : AAsyncCallContext < bool >
5124
5144
{
5125
5145
internal static readonly Func < Task , object , Task < bool > > s_execute = SqlDataReader . ReadAsyncExecute ;
5126
5146
@@ -5141,7 +5161,7 @@ public override void Dispose()
5141
5161
}
5142
5162
}
5143
5163
5144
- private sealed class IsDBNullAsyncCallContext : AAsyncCallContext < bool >
5164
+ internal sealed class IsDBNullAsyncCallContext : AAsyncCallContext < bool >
5145
5165
{
5146
5166
internal static readonly Func < Task , object , Task < bool > > s_execute = SqlDataReader . IsDBNullAsyncExecute ;
5147
5167
@@ -5390,7 +5410,14 @@ private void PrepareAsyncInvocation(bool useSnapshot)
5390
5410
5391
5411
if ( _snapshot == null )
5392
5412
{
5393
- _snapshot = Interlocked . Exchange ( ref _cachedSnapshot , null ) ?? new Snapshot ( ) ;
5413
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection )
5414
+ {
5415
+ _snapshot = Interlocked . Exchange ( ref sqlInternalConnection . CachedDataReaderSnapshot , null ) ?? new Snapshot ( ) ;
5416
+ }
5417
+ else
5418
+ {
5419
+ _snapshot = new Snapshot ( ) ;
5420
+ }
5394
5421
5395
5422
_snapshot . _dataReady = _sharedState . _dataReady ;
5396
5423
_snapshot . _haltRead = _haltRead ;
@@ -5463,9 +5490,9 @@ private void CleanupAfterAsyncInvocationInternal(TdsParserStateObject stateObj,
5463
5490
stateObj . _permitReplayStackTraceToDiffer = false ;
5464
5491
#endif
5465
5492
5466
- if ( _cachedSnapshot is null )
5493
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection && sqlInternalConnection . CachedDataReaderSnapshot is null )
5467
5494
{
5468
- _cachedSnapshot = _snapshot ;
5495
+ sqlInternalConnection . CachedDataReaderSnapshot = _snapshot ;
5469
5496
}
5470
5497
// 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)
5471
5498
_snapshot = null ;
@@ -5505,9 +5532,9 @@ private void SwitchToAsyncWithoutSnapshot()
5505
5532
Debug . Assert ( _snapshot != null , "Should currently have a snapshot" ) ;
5506
5533
Debug . Assert ( _stateObj != null && ! _stateObj . _asyncReadWithoutSnapshot , "Already in async without snapshot" ) ;
5507
5534
5508
- if ( _cachedSnapshot is null )
5535
+ if ( _connection ? . InnerConnection is SqlInternalConnection sqlInternalConnection && sqlInternalConnection . CachedDataReaderSnapshot is null )
5509
5536
{
5510
- _cachedSnapshot = _snapshot ;
5537
+ sqlInternalConnection . CachedDataReaderSnapshot = _snapshot ;
5511
5538
}
5512
5539
_snapshot = null ;
5513
5540
_stateObj . ResetSnapshot ( ) ;
0 commit comments