2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
// See the LICENSE file in the project root for more information.
4
4
5
+ using System ;
5
6
using System . Data ;
6
7
using System . Data . Common ;
7
8
using System . Diagnostics ;
@@ -15,6 +16,7 @@ namespace Microsoft.Data.ProviderBase
15
16
{
16
17
internal abstract partial class DbConnectionFactory
17
18
{
19
+ private static readonly Action < Task < DbConnectionInternal > , object > s_tryGetConnectionCompletedContinuation = TryGetConnectionCompletedContinuation ;
18
20
19
21
internal bool TryGetConnection ( DbConnection owningConnection , TaskCompletionSource < DbConnectionInternal > retry , DbConnectionOptions userOptions , DbConnectionInternal oldConnection , out DbConnectionInternal connection )
20
22
{
@@ -82,25 +84,7 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour
82
84
83
85
// now that we have an antecedent task, schedule our work when it is completed.
84
86
// If it is a new slot or a completed task, this continuation will start right away.
85
- newTask = s_pendingOpenNonPooled [ idx ] . ContinueWith ( ( _ ) =>
86
- {
87
- Transaction originalTransaction = ADP . GetCurrentTransaction ( ) ;
88
- try
89
- {
90
- ADP . SetCurrentTransaction ( retry . Task . AsyncState as Transaction ) ;
91
- var newConnection = CreateNonPooledConnection ( owningConnection , poolGroup , userOptions ) ;
92
- if ( ( oldConnection != null ) && ( oldConnection . State == ConnectionState . Open ) )
93
- {
94
- oldConnection . PrepareForReplaceConnection ( ) ;
95
- oldConnection . Dispose ( ) ;
96
- }
97
- return newConnection ;
98
- }
99
- finally
100
- {
101
- ADP . SetCurrentTransaction ( originalTransaction ) ;
102
- }
103
- } , cancellationTokenSource . Token , TaskContinuationOptions . LongRunning , TaskScheduler . Default ) ;
87
+ newTask = CreateReplaceConnectionContinuation ( s_pendingOpenNonPooled [ idx ] , owningConnection , retry , userOptions , oldConnection , poolGroup , cancellationTokenSource ) ;
104
88
105
89
// Place this new task in the slot so any future work will be queued behind it
106
90
s_pendingOpenNonPooled [ idx ] = newTask ;
@@ -114,29 +98,11 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour
114
98
}
115
99
116
100
// once the task is done, propagate the final results to the original caller
117
- newTask . ContinueWith ( ( task ) =>
118
- {
119
- cancellationTokenSource . Dispose ( ) ;
120
- if ( task . IsCanceled )
121
- {
122
- retry . TrySetException ( ADP . ExceptionWithStackTrace ( ADP . NonPooledOpenTimeout ( ) ) ) ;
123
- }
124
- else if ( task . IsFaulted )
125
- {
126
- retry . TrySetException ( task . Exception . InnerException ) ;
127
- }
128
- else
129
- {
130
- if ( ! retry . TrySetResult ( task . Result ) )
131
- {
132
- // The outer TaskCompletionSource was already completed
133
- // Which means that we don't know if someone has messed with the outer connection in the middle of creation
134
- // So the best thing to do now is to destroy the newly created connection
135
- task . Result . DoomThisConnection ( ) ;
136
- task . Result . Dispose ( ) ;
137
- }
138
- }
139
- } , TaskScheduler . Default ) ;
101
+ newTask . ContinueWith (
102
+ continuationAction : s_tryGetConnectionCompletedContinuation ,
103
+ state : Tuple . Create ( cancellationTokenSource , retry ) ,
104
+ scheduler : TaskScheduler . Default
105
+ ) ;
140
106
141
107
return false ;
142
108
}
@@ -188,5 +154,62 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour
188
154
189
155
return true ;
190
156
}
157
+
158
+ private Task < DbConnectionInternal > CreateReplaceConnectionContinuation ( Task < DbConnectionInternal > task , DbConnection owningConnection , TaskCompletionSource < DbConnectionInternal > retry , DbConnectionOptions userOptions , DbConnectionInternal oldConnection , DbConnectionPoolGroup poolGroup , CancellationTokenSource cancellationTokenSource )
159
+ {
160
+ return task . ContinueWith (
161
+ ( _ ) =>
162
+ {
163
+ Transaction originalTransaction = ADP . GetCurrentTransaction ( ) ;
164
+ try
165
+ {
166
+ ADP . SetCurrentTransaction ( retry . Task . AsyncState as Transaction ) ;
167
+ var newConnection = CreateNonPooledConnection ( owningConnection , poolGroup , userOptions ) ;
168
+ if ( ( oldConnection != null ) && ( oldConnection . State == ConnectionState . Open ) )
169
+ {
170
+ oldConnection . PrepareForReplaceConnection ( ) ;
171
+ oldConnection . Dispose ( ) ;
172
+ }
173
+ return newConnection ;
174
+ }
175
+ finally
176
+ {
177
+ ADP . SetCurrentTransaction ( originalTransaction ) ;
178
+ }
179
+ } ,
180
+ cancellationTokenSource . Token ,
181
+ TaskContinuationOptions . LongRunning ,
182
+ TaskScheduler . Default
183
+ ) ;
184
+ }
185
+
186
+ private static void TryGetConnectionCompletedContinuation ( Task < DbConnectionInternal > task , object state )
187
+ {
188
+ Tuple < CancellationTokenSource , TaskCompletionSource < DbConnectionInternal > > parameters = ( Tuple < CancellationTokenSource , TaskCompletionSource < DbConnectionInternal > > ) state ;
189
+ CancellationTokenSource source = parameters . Item1 ;
190
+ source . Dispose ( ) ;
191
+
192
+ TaskCompletionSource < DbConnectionInternal > retryCompletionSrouce = parameters . Item2 ;
193
+
194
+ if ( task . IsCanceled )
195
+ {
196
+ retryCompletionSrouce . TrySetException ( ADP . ExceptionWithStackTrace ( ADP . NonPooledOpenTimeout ( ) ) ) ;
197
+ }
198
+ else if ( task . IsFaulted )
199
+ {
200
+ retryCompletionSrouce . TrySetException ( task . Exception . InnerException ) ;
201
+ }
202
+ else
203
+ {
204
+ if ( ! retryCompletionSrouce . TrySetResult ( task . Result ) )
205
+ {
206
+ // The outer TaskCompletionSource was already completed
207
+ // Which means that we don't know if someone has messed with the outer connection in the middle of creation
208
+ // So the best thing to do now is to destroy the newly created connection
209
+ task . Result . DoomThisConnection ( ) ;
210
+ task . Result . Dispose ( ) ;
211
+ }
212
+ }
213
+ }
191
214
}
192
215
}
0 commit comments