22// The .NET Foundation licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for more information.
44
5+ using System ;
56using System . Diagnostics ;
7+ using System . Threading ;
68using Microsoft . Data . ProviderBase ;
79
810namespace Microsoft . Data . SqlClient
911{
1012 sealed internal class SqlReferenceCollection : DbReferenceCollection
1113 {
14+ private sealed class FindLiveReaderContext
15+ {
16+ public readonly Func < SqlDataReader , bool > Func ;
17+
18+ private SqlCommand _command ;
19+
20+ public FindLiveReaderContext ( ) => Func = Predicate ;
21+
22+ public void Setup ( SqlCommand command ) => _command = command ;
23+
24+ public void Clear ( ) => _command = null ;
25+
26+ private bool Predicate ( SqlDataReader reader ) => ( ! reader . IsClosed ) && ( _command == reader . Command ) ;
27+ }
28+
1229 internal const int DataReaderTag = 1 ;
1330 internal const int CommandTag = 2 ;
1431 internal const int BulkCopyTag = 3 ;
1532
16- override public void Add ( object value , int tag )
33+ private readonly static Func < SqlDataReader , bool > s_hasOpenReaderFunc = HasOpenReaderPredicate ;
34+ private static FindLiveReaderContext s_cachedFindLiveReaderContext ;
35+
36+ public override void Add ( object value , int tag )
1737 {
1838 Debug . Assert ( DataReaderTag == tag || CommandTag == tag || BulkCopyTag == tag , "unexpected tag?" ) ;
1939 Debug . Assert ( DataReaderTag != tag || value is SqlDataReader , "tag doesn't match object type: SqlDataReader" ) ;
@@ -30,25 +50,24 @@ internal void Deactivate()
3050
3151 internal SqlDataReader FindLiveReader ( SqlCommand command )
3252 {
33- if ( command == null )
53+ if ( command is null )
3454 {
3555 // if null == command, will find first live datareader
36- return FindItem < SqlDataReader > ( DataReaderTag , ( dataReader ) => ( ! dataReader . IsClosed ) ) ;
56+ return FindItem ( DataReaderTag , s_hasOpenReaderFunc ) ;
3757 }
3858 else
3959 {
4060 // else will find live datareader associated with the command
41- return FindItem < SqlDataReader > ( DataReaderTag , ( dataReader ) => ( ( ! dataReader . IsClosed ) && ( command == dataReader . Command ) ) ) ;
61+ FindLiveReaderContext context = Interlocked . Exchange ( ref s_cachedFindLiveReaderContext , null ) ?? new FindLiveReaderContext ( ) ;
62+ context . Setup ( command ) ;
63+ SqlDataReader retval = FindItem ( DataReaderTag , context . Func ) ;
64+ context . Clear ( ) ;
65+ Interlocked . CompareExchange ( ref s_cachedFindLiveReaderContext , context , null ) ;
66+ return retval ;
4267 }
4368 }
4469
45- // Finds a SqlCommand associated with the given StateObject
46- internal SqlCommand FindLiveCommand ( TdsParserStateObject stateObj )
47- {
48- return FindItem < SqlCommand > ( CommandTag , ( command ) => ( command . StateObject == stateObj ) ) ;
49- }
50-
51- override protected void NotifyItem ( int message , int tag , object value )
70+ protected override void NotifyItem ( int message , int tag , object value )
5271 {
5372 Debug . Assert ( 0 == message , "unexpected message?" ) ;
5473 Debug . Assert ( DataReaderTag == tag || CommandTag == tag || BulkCopyTag == tag , "unexpected tag?" ) ;
@@ -74,11 +93,13 @@ override protected void NotifyItem(int message, int tag, object value)
7493 }
7594 }
7695
77- override public void Remove ( object value )
96+ public override void Remove ( object value )
7897 {
7998 Debug . Assert ( value is SqlDataReader || value is SqlCommand || value is SqlBulkCopy , "SqlReferenceCollection.Remove expected a SqlDataReader or SqlCommand or SqlBulkCopy" ) ;
8099
81100 base . RemoveItem ( value ) ;
82101 }
102+
103+ private static bool HasOpenReaderPredicate ( SqlDataReader reader ) => ! reader . IsClosed ;
83104 }
84105}
0 commit comments