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 . Data ;
67using System . Data . Common ;
78using System . Diagnostics ;
@@ -15,6 +16,7 @@ namespace Microsoft.Data.ProviderBase
1516{
1617 internal abstract partial class DbConnectionFactory
1718 {
19+ private static readonly Action < Task < DbConnectionInternal > , object > s_tryGetConnectionCompletedContinuation = TryGetConnectionCompletedContinuation ;
1820
1921 internal bool TryGetConnection ( DbConnection owningConnection , TaskCompletionSource < DbConnectionInternal > retry , DbConnectionOptions userOptions , DbConnectionInternal oldConnection , out DbConnectionInternal connection )
2022 {
@@ -82,25 +84,7 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour
8284
8385 // now that we have an antecedent task, schedule our work when it is completed.
8486 // 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 ) ;
10488
10589 // Place this new task in the slot so any future work will be queued behind it
10690 s_pendingOpenNonPooled [ idx ] = newTask ;
@@ -114,29 +98,11 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour
11498 }
11599
116100 // 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+ ) ;
140106
141107 return false ;
142108 }
@@ -188,5 +154,62 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour
188154
189155 return true ;
190156 }
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+ }
191214 }
192215}
0 commit comments