7
7
using System . Threading ;
8
8
using System . Threading . Tasks ;
9
9
using System . Transactions ;
10
+ using Microsoft . Data . Common ;
10
11
using Microsoft . Data . Common . ConnectionString ;
11
12
using Microsoft . Data . ProviderBase ;
12
13
using Microsoft . Data . SqlClient . ConnectionPool ;
@@ -16,17 +17,19 @@ namespace Microsoft.Data.SqlClient.UnitTests
16
17
{
17
18
public class ChannelDbConnectionPoolTest
18
19
{
19
- private readonly ChannelDbConnectionPool pool ;
20
- private readonly DbConnectionPoolGroup dbConnectionPoolGroup ;
21
- private readonly DbConnectionPoolGroupOptions poolGroupOptions ;
22
- private readonly DbConnectionFactory connectionFactory ;
23
- private readonly DbConnectionPoolIdentity identity ;
24
- private readonly DbConnectionPoolProviderInfo connectionPoolProviderInfo ;
25
-
26
- public ChannelDbConnectionPoolTest ( )
20
+ private ChannelDbConnectionPool pool ;
21
+ private DbConnectionFactory connectionFactory ;
22
+ private DbConnectionPoolGroup dbConnectionPoolGroup ;
23
+ private DbConnectionPoolGroupOptions poolGroupOptions ;
24
+ private DbConnectionPoolIdentity identity ;
25
+ private DbConnectionPoolProviderInfo connectionPoolProviderInfo ;
26
+
27
+ private static readonly DbConnectionFactory SuccessfulConnectionFactory = new SuccessfulDbConnectionFactory ( ) ;
28
+ private static readonly DbConnectionFactory TimeoutConnectionFactory = new TimeoutDbConnectionFactory ( ) ;
29
+
30
+ private void Setup ( DbConnectionFactory connectionFactory )
27
31
{
28
- // Use a stubbed connection factory to avoid network code
29
- connectionFactory = new SuccessfulConnectionFactory ( ) ;
32
+ this . connectionFactory = connectionFactory ;
30
33
identity = DbConnectionPoolIdentity . NoIdentity ;
31
34
connectionPoolProviderInfo = new DbConnectionPoolProviderInfo ( ) ;
32
35
poolGroupOptions = new DbConnectionPoolGroupOptions (
@@ -54,8 +57,11 @@ public ChannelDbConnectionPoolTest()
54
57
[ InlineData ( 1 ) ]
55
58
[ InlineData ( 5 ) ]
56
59
[ InlineData ( 10 ) ]
57
- public void TestGetConnectionFromEmptyPoolSync_ShouldCreateNewConnection ( int numConnections )
60
+ public void GetConnectionFromEmptyPoolSync_ShouldCreateNewConnection ( int numConnections )
58
61
{
62
+ // Arrange
63
+ Setup ( SuccessfulConnectionFactory ) ;
64
+
59
65
// Act
60
66
for ( int i = 0 ; i < numConnections ; i ++ )
61
67
{
@@ -81,8 +87,11 @@ out internalConnection
81
87
[ InlineData ( 1 ) ]
82
88
[ InlineData ( 5 ) ]
83
89
[ InlineData ( 10 ) ]
84
- public async Task TestGetConnectionFromEmptyPoolAsync_ShouldCreateNewConnection ( int numConnections )
90
+ public async Task GetConnectionFromEmptyPoolAsync_ShouldCreateNewConnection ( int numConnections )
85
91
{
92
+ // Arrange
93
+ Setup ( SuccessfulConnectionFactory ) ;
94
+
86
95
// Act
87
96
for ( int i = 0 ; i < numConnections ; i ++ )
88
97
{
@@ -106,8 +115,106 @@ out internalConnection
106
115
Assert . Equal ( numConnections , pool . Count ) ;
107
116
}
108
117
109
- // Test that requests to get connection from the pool fails when max pool size is reached
110
-
118
+ [ Fact ]
119
+ public void GetConnectionRespectsMaxPoolSize ( )
120
+ {
121
+ // Arrange
122
+ Setup ( SuccessfulConnectionFactory ) ;
123
+
124
+ for ( int i = 0 ; i < poolGroupOptions . MaxPoolSize ; i ++ )
125
+ {
126
+ DbConnectionInternal internalConnection = null ;
127
+ var completed = pool . TryGetConnection (
128
+ new SqlConnection ( ) ,
129
+ taskCompletionSource : null ,
130
+ new DbConnectionOptions ( "" , null ) ,
131
+ out internalConnection
132
+ ) ;
133
+
134
+ Assert . True ( completed ) ;
135
+ Assert . NotNull ( internalConnection ) ;
136
+ }
137
+
138
+ try
139
+ {
140
+ // Act
141
+ DbConnectionInternal extraConnection = null ;
142
+ var exceeded = pool . TryGetConnection (
143
+ new SqlConnection ( "Timeout=1" ) ,
144
+ taskCompletionSource : null ,
145
+ new DbConnectionOptions ( "" , null ) ,
146
+ out extraConnection
147
+ ) ;
148
+ }
149
+ catch ( Exception ex )
150
+ {
151
+ // Assert
152
+ Assert . IsType < InvalidOperationException > ( ex ) ;
153
+ Assert . Equal ( "Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached." , ex . Message ) ;
154
+ }
155
+
156
+ // Assert
157
+ Assert . Equal ( poolGroupOptions . MaxPoolSize , pool . Count ) ;
158
+ }
159
+
160
+ [ Fact ]
161
+ [ ActiveIssue ( "Requires ReturnInternalConnection to be implemented in ChannelDbConnectionPool" ) ]
162
+ public void ConnectionsAreReused ( )
163
+ {
164
+ // Arrange
165
+ Setup ( SuccessfulConnectionFactory ) ;
166
+ DbConnectionInternal internalConnection1 = null ;
167
+ DbConnectionInternal internalConnection2 = null ;
168
+
169
+ // Act: Get the first connection
170
+ var completed1 = pool . TryGetConnection (
171
+ new SqlConnection ( ) ,
172
+ null ,
173
+ new DbConnectionOptions ( "" , null ) ,
174
+ out internalConnection1
175
+ ) ;
176
+
177
+ // Assert: First connection should succeed
178
+ Assert . True ( completed1 ) ;
179
+ Assert . NotNull ( internalConnection1 ) ;
180
+
181
+ // Act: Return the first connection to the pool
182
+ pool . ReturnInternalConnection ( internalConnection1 , null ) ;
183
+
184
+ // Act: Get the second connection (should reuse the first one)
185
+ var completed2 = pool . TryGetConnection (
186
+ new SqlConnection ( ) ,
187
+ null ,
188
+ new DbConnectionOptions ( "" , null ) ,
189
+ out internalConnection2
190
+ ) ;
191
+
192
+ // Assert: Second connection should succeed and reuse the first connection
193
+ Assert . True ( completed2 ) ;
194
+ Assert . NotNull ( internalConnection2 ) ;
195
+ Assert . Same ( internalConnection1 , internalConnection2 ) ;
196
+ }
197
+
198
+ [ Fact ]
199
+ public void GetConnectionWithTimeout_ShouldThrowTimeoutException ( )
200
+ {
201
+ // Arrange
202
+ Setup ( TimeoutConnectionFactory ) ;
203
+ DbConnectionInternal internalConnection = null ;
204
+
205
+ // Act & Assert
206
+ var ex = Assert . Throws < InvalidOperationException > ( ( ) =>
207
+ {
208
+ var completed = pool . TryGetConnection (
209
+ new SqlConnection ( ) ,
210
+ taskCompletionSource : null ,
211
+ new DbConnectionOptions ( "" , null ) ,
212
+ out internalConnection
213
+ ) ;
214
+ } ) ;
215
+
216
+ Assert . Equal ( "Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached." , ex . Message ) ;
217
+ }
111
218
112
219
#region Property Tests
113
220
@@ -243,49 +350,10 @@ public void TestTransactionEnded()
243
350
{
244
351
Assert . Throws < NotImplementedException > ( ( ) => pool . TransactionEnded ( null ! , null ! ) ) ;
245
352
}
246
- [ Fact ]
247
- public void TestGetConnectionFailsWhenMaxPoolSizeReached ( )
248
- {
249
- // Arrange
250
- for ( int i = 0 ; i < poolGroupOptions . MaxPoolSize ; i ++ )
251
- {
252
- DbConnectionInternal internalConnection = null ;
253
- var completed = pool . TryGetConnection (
254
- new SqlConnection ( ) ,
255
- taskCompletionSource : null ,
256
- new DbConnectionOptions ( "" , null ) ,
257
- out internalConnection
258
- ) ;
259
-
260
- Assert . True ( completed ) ;
261
- Assert . NotNull ( internalConnection ) ;
262
- }
263
-
264
- try
265
- {
266
- // Act
267
- DbConnectionInternal extraConnection = null ;
268
- var exceeded = pool . TryGetConnection (
269
- //TODO: set timeout to make this faster
270
- new SqlConnection ( ) ,
271
- taskCompletionSource : null ,
272
- new DbConnectionOptions ( "" , null ) ,
273
- out extraConnection
274
- ) ;
275
- } catch ( Exception ex )
276
- {
277
- // Assert
278
- Assert . IsType < InvalidOperationException > ( ex ) ;
279
- Assert . Equal ( "Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached." , ex . Message ) ;
280
- }
281
-
282
- // Assert
283
- Assert . Equal ( poolGroupOptions . MaxPoolSize , pool . Count ) ;
284
- }
285
353
#endregion
286
354
287
355
#region Test classes
288
- internal class SuccessfulConnectionFactory : DbConnectionFactory
356
+ internal class SuccessfulDbConnectionFactory : DbConnectionFactory
289
357
{
290
358
protected override DbConnectionInternal CreateConnection ( DbConnectionOptions options , DbConnectionPoolKey poolKey , object poolGroupProviderInfo , IDbConnectionPool pool , DbConnection owningConnection )
291
359
{
@@ -347,89 +415,89 @@ internal override void SetInnerConnectionTo(DbConnection owningObject, DbConnect
347
415
#endregion
348
416
}
349
417
350
- internal class SuccessfulDbConnectionInternal : DbConnectionInternal
418
+ internal class TimeoutDbConnectionFactory : DbConnectionFactory
351
419
{
352
- #region Not Implemented Members
353
- public override string ServerVersion => throw new NotImplementedException ( ) ;
354
-
355
- public override DbTransaction BeginTransaction ( System . Data . IsolationLevel il )
420
+ protected override DbConnectionInternal CreateConnection ( DbConnectionOptions options , DbConnectionPoolKey poolKey , object poolGroupProviderInfo , IDbConnectionPool pool , DbConnection owningConnection )
356
421
{
357
- throw new NotImplementedException ( ) ;
422
+ throw ADP . PooledOpenTimeout ( ) ;
358
423
}
359
424
360
- public override void EnlistTransaction ( Transaction transaction )
425
+ #region Not Implemented Members
426
+ public override DbProviderFactory ProviderFactory => throw new NotImplementedException ( ) ;
427
+
428
+ protected override DbConnectionOptions CreateConnectionOptions ( string connectionString , DbConnectionOptions previous )
361
429
{
362
430
throw new NotImplementedException ( ) ;
363
431
}
364
432
365
- protected override void Activate ( Transaction transaction )
433
+ protected override DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions ( DbConnectionOptions options )
366
434
{
367
435
throw new NotImplementedException ( ) ;
368
436
}
369
437
370
- protected override void Deactivate ( )
438
+ protected override int GetObjectId ( DbConnection connection )
371
439
{
372
440
throw new NotImplementedException ( ) ;
373
441
}
374
- #endregion
375
- }
376
442
377
- internal class TimeoutConnectionFactory : DbConnectionFactory
378
- {
379
- protected override DbConnectionInternal CreateConnection ( DbConnectionOptions options , DbConnectionPoolKey poolKey , object poolGroupProviderInfo , IDbConnectionPool pool , DbConnection owningConnection )
443
+ internal override DbConnectionPoolGroup GetConnectionPoolGroup ( DbConnection connection )
380
444
{
381
- return new SuccessfulDbConnectionInternal ( ) ;
445
+ throw new NotImplementedException ( ) ;
382
446
}
383
447
384
- #region Not Implemented Members
385
- public override DbProviderFactory ProviderFactory => throw new NotImplementedException ( ) ;
386
-
387
- protected override DbConnectionOptions CreateConnectionOptions ( string connectionString , DbConnectionOptions previous )
448
+ internal override DbConnectionInternal GetInnerConnection ( DbConnection connection )
388
449
{
389
450
throw new NotImplementedException ( ) ;
390
451
}
391
452
392
- protected override DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions ( DbConnectionOptions options )
453
+ internal override void PermissionDemand ( DbConnection outerConnection )
393
454
{
394
455
throw new NotImplementedException ( ) ;
395
456
}
396
457
397
- protected override int GetObjectId ( DbConnection connection )
458
+ internal override void SetConnectionPoolGroup ( DbConnection outerConnection , DbConnectionPoolGroup poolGroup )
398
459
{
399
460
throw new NotImplementedException ( ) ;
400
461
}
401
462
402
- internal override DbConnectionPoolGroup GetConnectionPoolGroup ( DbConnection connection )
463
+ internal override void SetInnerConnectionEvent ( DbConnection owningObject , DbConnectionInternal to )
403
464
{
404
465
throw new NotImplementedException ( ) ;
405
466
}
406
467
407
- internal override DbConnectionInternal GetInnerConnection ( DbConnection connection )
468
+ internal override bool SetInnerConnectionFrom ( DbConnection owningObject , DbConnectionInternal to , DbConnectionInternal from )
408
469
{
409
470
throw new NotImplementedException ( ) ;
410
471
}
411
472
412
- internal override void PermissionDemand ( DbConnection outerConnection )
473
+ internal override void SetInnerConnectionTo ( DbConnection owningObject , DbConnectionInternal to )
413
474
{
414
475
throw new NotImplementedException ( ) ;
415
476
}
477
+ #endregion
478
+ }
416
479
417
- internal override void SetConnectionPoolGroup ( DbConnection outerConnection , DbConnectionPoolGroup poolGroup )
480
+ internal class SuccessfulDbConnectionInternal : DbConnectionInternal
481
+ {
482
+ #region Not Implemented Members
483
+ public override string ServerVersion => throw new NotImplementedException ( ) ;
484
+
485
+ public override DbTransaction BeginTransaction ( System . Data . IsolationLevel il )
418
486
{
419
487
throw new NotImplementedException ( ) ;
420
488
}
421
489
422
- internal override void SetInnerConnectionEvent ( DbConnection owningObject , DbConnectionInternal to )
490
+ public override void EnlistTransaction ( Transaction transaction )
423
491
{
424
492
throw new NotImplementedException ( ) ;
425
493
}
426
494
427
- internal override bool SetInnerConnectionFrom ( DbConnection owningObject , DbConnectionInternal to , DbConnectionInternal from )
495
+ protected override void Activate ( Transaction transaction )
428
496
{
429
497
throw new NotImplementedException ( ) ;
430
498
}
431
499
432
- internal override void SetInnerConnectionTo ( DbConnection owningObject , DbConnectionInternal to )
500
+ protected override void Deactivate ( )
433
501
{
434
502
throw new NotImplementedException ( ) ;
435
503
}
0 commit comments