1 //------------------------------------------------------------------------------
2 // <copyright file="DbConnectionFactory.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
9 namespace System.Data.ProviderBase {
12 using System.Collections.Generic;
13 using System.Diagnostics;
14 using System.Data.Common;
16 using System.Threading;
17 using System.Threading.Tasks;
19 internal abstract class DbConnectionFactory {
20 private Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> _connectionPoolGroups;
21 private readonly List<DbConnectionPool> _poolsToRelease;
22 private readonly List<DbConnectionPoolGroup> _poolGroupsToRelease;
23 private readonly DbConnectionPoolCounters _performanceCounters;
24 private readonly Timer _pruningTimer;
26 private const int PruningDueTime =4*60*1000; // 4 minutes
27 private const int PruningPeriod = 30*1000; // thirty seconds
29 private static int _objectTypeCount; // Bid counter
30 internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
32 // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to
33 // a maximum of Environment.ProcessorCount at a time.
34 static int s_pendingOpenNonPooledNext = 0;
35 static Task<DbConnectionInternal>[] s_pendingOpenNonPooled = new Task<DbConnectionInternal>[Environment.ProcessorCount];
36 static Task<DbConnectionInternal> s_completedTask;
38 protected DbConnectionFactory() : this (DbConnectionPoolCountersNoCounters.SingletonInstance) { }
40 protected DbConnectionFactory(DbConnectionPoolCounters performanceCounters) {
41 _performanceCounters = performanceCounters;
42 _connectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>();
43 _poolsToRelease = new List<DbConnectionPool>();
44 _poolGroupsToRelease = new List<DbConnectionPoolGroup>();
45 _pruningTimer = CreatePruningTimer();
48 internal DbConnectionPoolCounters PerformanceCounters {
49 get { return _performanceCounters; }
52 abstract public DbProviderFactory ProviderFactory {
56 internal int ObjectID {
62 public void ClearAllPools() {
64 Bid.ScopeEnter(out hscp, "<prov.DbConnectionFactory.ClearAllPools|API> ");
66 Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
67 foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> entry in connectionPoolGroups) {
68 DbConnectionPoolGroup poolGroup = entry.Value;
69 if (null != poolGroup) {
75 Bid.ScopeLeave(ref hscp);
79 public void ClearPool(DbConnection connection) {
80 ADP.CheckArgumentNull(connection, "connection");
83 Bid.ScopeEnter(out hscp, "<prov.DbConnectionFactory.ClearPool|API> %d#" , GetObjectId(connection));
85 DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection);
86 if (null != poolGroup) {
91 Bid.ScopeLeave(ref hscp);
95 public void ClearPool(DbConnectionPoolKey key) {
96 Debug.Assert(key != null, "key cannot be null");
97 ADP.CheckArgumentNull(key.ConnectionString, "key.ConnectionString");
100 Bid.ScopeEnter(out hscp, "<prov.DbConnectionFactory.ClearPool|API> connectionString");
102 DbConnectionPoolGroup poolGroup;
103 Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
104 if (connectionPoolGroups.TryGetValue(key, out poolGroup)) {
109 Bid.ScopeLeave(ref hscp);
113 internal virtual DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions){
117 virtual protected DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory) {
118 // providers that support GetSchema must override this with a method that creates a meta data
119 // factory appropriate for them.
120 cacheMetaDataFactory = false;
121 throw ADP.NotSupported();
124 internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup, DbConnectionOptions userOptions) {
125 Debug.Assert(null != owningConnection, "null owningConnection?");
126 Debug.Assert(null != poolGroup, "null poolGroup?");
128 DbConnectionOptions connectionOptions = poolGroup.ConnectionOptions;
129 DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = poolGroup.ProviderInfo;
130 DbConnectionPoolKey poolKey = poolGroup.PoolKey;
132 DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolKey, poolGroupProviderInfo, null, owningConnection, userOptions);
133 if (null != newConnection) {
135 PerformanceCounters.HardConnectsPerSecond.Increment();
137 newConnection.MakeNonPooledObject(owningConnection, PerformanceCounters);
139 Bid.Trace("<prov.DbConnectionFactory.CreateNonPooledConnection|RES|CPOOL> %d#, Non-pooled database connection created.\n", ObjectID);
140 return newConnection;
143 internal DbConnectionInternal CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions) {
144 Debug.Assert(null != pool, "null pool?");
145 DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = pool.PoolGroup.ProviderInfo;
147 DbConnectionInternal newConnection = CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningObject, userOptions);
148 if (null != newConnection) {
150 PerformanceCounters.HardConnectsPerSecond.Increment();
152 newConnection.MakePooledConnection(pool);
154 Bid.Trace("<prov.DbConnectionFactory.CreatePooledConnection|RES|CPOOL> %d#, Pooled database connection created.\n", ObjectID);
155 return newConnection;
158 virtual internal DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo (DbConnectionOptions connectionOptions) {
162 private Timer CreatePruningTimer() {
163 TimerCallback callback = new TimerCallback(PruneConnectionPoolGroups);
164 return new Timer(callback, null, PruningDueTime, PruningPeriod);
167 protected DbConnectionOptions FindConnectionOptions(DbConnectionPoolKey key) {
168 Debug.Assert(key != null, "key cannot be null");
169 if (!ADP.IsEmpty(key.ConnectionString)) {
170 DbConnectionPoolGroup connectionPoolGroup;
171 Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
172 if (connectionPoolGroups.TryGetValue(key, out connectionPoolGroup)) {
173 return connectionPoolGroup.ConnectionOptions;
179 // GetCompletedTask must be called from within s_pendingOpenPooled lock
180 static Task<DbConnectionInternal> GetCompletedTask()
182 if (s_completedTask == null) {
183 TaskCompletionSource<DbConnectionInternal> source = new TaskCompletionSource<DbConnectionInternal>();
184 source.SetResult(null);
185 s_completedTask = source.Task;
187 return s_completedTask;
190 internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, out DbConnectionInternal connection) {
191 Debug.Assert(null != owningConnection, "null owningConnection?");
193 DbConnectionPoolGroup poolGroup;
194 DbConnectionPool connectionPool;
198 // Work around race condition with clearing the pool between GetConnectionPool obtaining pool
199 // and GetConnection on the pool checking the pool state. Clearing the pool in this window
200 // will switch the pool into the ShuttingDown state, and GetConnection will return null.
201 // There is probably a better solution involving locking the pool/group, but that entails a major
202 // re-design of the connection pooling synchronization, so is post-poned for now.
204 // VSDD 674236: use retriesLeft to prevent CPU spikes with incremental sleep
205 // start with one msec, double the time every retry
206 // max time is: 1 + 2 + 4 + ... + 2^(retries-1) == 2^retries -1 == 1023ms (for 10 retries)
207 int retriesLeft = 10;
208 int timeBetweenRetriesMilliseconds = 1;
211 poolGroup = GetConnectionPoolGroup(owningConnection);
212 // Doing this on the callers thread is important because it looks up the WindowsIdentity from the thread.
213 connectionPool = GetConnectionPool(owningConnection, poolGroup);
214 if (null == connectionPool) {
215 // If GetConnectionPool returns null, we can be certain that
216 // this connection should not be pooled via DbConnectionPool
217 // or have a disabled pool entry.
218 poolGroup = GetConnectionPoolGroup(owningConnection); // previous entry have been disabled
221 Task<DbConnectionInternal> newTask;
222 CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
223 lock (s_pendingOpenNonPooled) {
225 // look for an available task slot (completed or empty)
227 for (idx=0; idx<s_pendingOpenNonPooled.Length; idx++) {
228 Task task = s_pendingOpenNonPooled[idx];
230 s_pendingOpenNonPooled[idx] = GetCompletedTask();
233 else if (task.IsCompleted) {
238 // if didn't find one, pick the next one in round-robbin fashion
239 if (idx == s_pendingOpenNonPooled.Length) {
240 idx = s_pendingOpenNonPooledNext++ % s_pendingOpenNonPooled.Length;
243 // now that we have an antecedent task, schedule our work when it is completed.
244 // If it is a new slot or a compelted task, this continuation will start right away.
248 newTask = s_pendingOpenNonPooled[idx].ContinueWith((_) => {
249 Transactions.Transaction originalTransaction = ADP.GetCurrentTransaction();
252 ADP.SetCurrentTransaction(retry.Task.AsyncState as Transactions.Transaction);
253 var newConnection = CreateNonPooledConnection(owningConnection, poolGroup, userOptions);
254 if ((oldConnection != null) && (oldConnection.State == ConnectionState.Open)) {
255 oldConnection.PrepareForReplaceConnection();
256 oldConnection.Dispose();
258 return newConnection;
260 ADP.SetCurrentTransaction(originalTransaction);
262 }, cancellationTokenSource.Token, TaskContinuationOptions.LongRunning, TaskScheduler.Default);
264 // Place this new task in the slot so any future work will be queued behind it
265 s_pendingOpenNonPooled[idx] = newTask;
268 // Set up the timeout (if needed)
269 if (owningConnection.ConnectionTimeout > 0) {
270 int connectionTimeoutMilliseconds = owningConnection.ConnectionTimeout * 1000;
271 cancellationTokenSource.CancelAfter(connectionTimeoutMilliseconds);
274 // once the task is done, propagate the final results to the original caller
275 newTask.ContinueWith((task) => {
276 cancellationTokenSource.Dispose();
277 if (task.IsCanceled) {
278 retry.TrySetException(ADP.ExceptionWithStackTrace(ADP.NonPooledOpenTimeout()));
279 } else if (task.IsFaulted) {
280 retry.TrySetException(task.Exception.InnerException);
283 if (retry.TrySetResult(task.Result)) {
285 PerformanceCounters.NumberOfNonPooledConnections.Increment();
289 // The outer TaskCompletionSource was already completed
290 // Which means that we don't know if someone has messed with the outer connection in the middle of creation
291 // So the best thing to do now is to destroy the newly created connection
292 task.Result.DoomThisConnection();
293 task.Result.Dispose();
296 }, TaskScheduler.Default);
301 connection = CreateNonPooledConnection(owningConnection, poolGroup, userOptions);
303 PerformanceCounters.NumberOfNonPooledConnections.Increment();
307 if (owningConnection.ForceNewConnection) {
308 Debug.Assert(!(oldConnection is DbConnectionClosed), "Force new connection, but there is no old connection");
309 connection = connectionPool.ReplaceConnection(owningConnection, userOptions, oldConnection);
312 if (!connectionPool.TryGetConnection(owningConnection, retry, userOptions, out connection)) {
317 if (connection == null) {
318 // connection creation failed on semaphore waiting or if max pool reached
319 if (connectionPool.IsRunning) {
320 // If GetConnection failed while the pool is running, the pool timeout occurred.
321 Bid.Trace("<prov.DbConnectionFactory.GetConnection|RES|CPOOL> %d#, GetConnection failed because a pool timeout occurred.\n", ObjectID);
322 throw ADP.PooledOpenTimeout();
325 // We've hit the race condition, where the pool was shut down after we got it from the group.
326 // Yield time slice to allow shut down activities to complete and a new, running pool to be instantiated
328 Threading.Thread.Sleep(timeBetweenRetriesMilliseconds);
329 timeBetweenRetriesMilliseconds *= 2; // double the wait time for next iteration
333 } while (connection == null && retriesLeft-- > 0);
335 if (connection == null) {
336 // exhausted all retries or timed out - give up
337 Bid.Trace("<prov.DbConnectionFactory.GetConnection|RES|CPOOL> %d#, GetConnection failed because a pool timeout occurred and all retries were exhausted.\n", ObjectID);
338 throw ADP.PooledOpenTimeout();
344 private DbConnectionPool GetConnectionPool(DbConnection owningObject, DbConnectionPoolGroup connectionPoolGroup) {
345 // if poolgroup is disabled, it will be replaced with a new entry
347 Debug.Assert(null != owningObject, "null owningObject?");
348 Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?");
350 // It is possible that while the outer connection object has
351 // been sitting around in a closed and unused state in some long
352 // running app, the pruner may have come along and remove this
353 // the pool entry from the master list. If we were to use a
354 // pool entry in this state, we would create "unmanaged" pools,
355 // which would be bad. To avoid this problem, we automagically
356 // re-create the pool entry whenever it's disabled.
358 // however, don't rebuild connectionOptions if no pooling is involved - let new connections do that work
359 if (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions)) {
360 Bid.Trace("<prov.DbConnectionFactory.GetConnectionPool|RES|INFO|CPOOL> %d#, DisabledPoolGroup=%d#\n", ObjectID, connectionPoolGroup.ObjectID);
362 // reusing existing pool option in case user originally used SetConnectionPoolOptions
363 DbConnectionPoolGroupOptions poolOptions = connectionPoolGroup.PoolGroupOptions;
365 // get the string to hash on again
366 DbConnectionOptions connectionOptions = connectionPoolGroup.ConnectionOptions;
367 Debug.Assert(null != connectionOptions, "prevent expansion of connectionString");
369 connectionPoolGroup = GetConnectionPoolGroup(connectionPoolGroup.PoolKey, poolOptions, ref connectionOptions);
370 Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?");
371 SetConnectionPoolGroup(owningObject, connectionPoolGroup);
373 DbConnectionPool connectionPool = connectionPoolGroup.GetConnectionPool(this);
374 return connectionPool;
377 internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolOptions, ref DbConnectionOptions userConnectionOptions) {
378 if (ADP.IsEmpty(key.ConnectionString)) {
379 return (DbConnectionPoolGroup)null;
382 DbConnectionPoolGroup connectionPoolGroup;
383 Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
384 if (!connectionPoolGroups.TryGetValue(key, out connectionPoolGroup) || (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions))) {
385 // If we can't find an entry for the connection string in
386 // our collection of pool entries, then we need to create a
387 // new pool entry and add it to our collection.
389 DbConnectionOptions connectionOptions = CreateConnectionOptions(key.ConnectionString, userConnectionOptions);
390 if (null == connectionOptions) {
391 throw ADP.InternalConnectionError(ADP.ConnectionError.ConnectionOptionsMissing);
394 string expandedConnectionString = key.ConnectionString;
395 if (null == userConnectionOptions) { // we only allow one expansion on the connection string
397 userConnectionOptions = connectionOptions;
398 expandedConnectionString = connectionOptions.Expand();
400 // if the expanded string is same instance (default implementation), the use the already created options
401 if ((object)expandedConnectionString != (object)key.ConnectionString) {
403 DbConnectionPoolKey newKey = (DbConnectionPoolKey) ((ICloneable) key).Clone();
404 newKey.ConnectionString = expandedConnectionString;
405 return GetConnectionPoolGroup(newKey, null, ref userConnectionOptions);
409 // We don't support connection pooling on Win9x; it lacks too many of the APIs we require.
410 if ((null == poolOptions) && ADP.IsWindowsNT) {
411 if (null != connectionPoolGroup) {
412 // reusing existing pool option in case user originally used SetConnectionPoolOptions
413 poolOptions = connectionPoolGroup.PoolGroupOptions;
416 // Note: may return null for non-pooled connections
417 poolOptions = CreateConnectionPoolGroupOptions(connectionOptions);
422 DbConnectionPoolGroup newConnectionPoolGroup = new DbConnectionPoolGroup(connectionOptions, key, poolOptions);
423 newConnectionPoolGroup.ProviderInfo = CreateConnectionPoolGroupProviderInfo(connectionOptions);
426 connectionPoolGroups = _connectionPoolGroups;
427 if (!connectionPoolGroups.TryGetValue(key, out connectionPoolGroup)) {
428 // build new dictionary with space for new connection string
429 Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> newConnectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>(1+connectionPoolGroups.Count);
430 foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> entry in connectionPoolGroups) {
431 newConnectionPoolGroups.Add(entry.Key, entry.Value);
434 // lock prevents race condition with PruneConnectionPoolGroups
435 newConnectionPoolGroups.Add(key, newConnectionPoolGroup);
437 PerformanceCounters.NumberOfActiveConnectionPoolGroups.Increment();
439 connectionPoolGroup = newConnectionPoolGroup;
440 _connectionPoolGroups = newConnectionPoolGroups;
443 Debug.Assert(!connectionPoolGroup.IsDisabled, "Disabled pool entry discovered");
446 Debug.Assert(null != connectionPoolGroup, "how did we not create a pool entry?");
447 Debug.Assert(null != userConnectionOptions, "how did we not have user connection options?");
449 else if (null == userConnectionOptions) {
450 userConnectionOptions = connectionPoolGroup.ConnectionOptions;
452 return connectionPoolGroup;
455 internal DbMetaDataFactory GetMetaDataFactory(DbConnectionPoolGroup connectionPoolGroup,DbConnectionInternal internalConnection){
456 Debug.Assert (connectionPoolGroup != null, "connectionPoolGroup may not be null.");
458 // get the matadatafactory from the pool entry. If it does not already have one
459 // create one and save it on the pool entry
460 DbMetaDataFactory metaDataFactory = connectionPoolGroup.MetaDataFactory;
462 // consider serializing this so we don't construct multiple metadata factories
463 // if two threads happen to hit this at the same time. One will be GC'd
464 if (metaDataFactory == null){
465 bool allowCache = false;
466 metaDataFactory = CreateMetaDataFactory(internalConnection, out allowCache);
468 connectionPoolGroup.MetaDataFactory = metaDataFactory;
471 return metaDataFactory;
474 private void PruneConnectionPoolGroups(object state) {
475 // when debugging this method, expect multiple threads at the same time
476 if (Bid.AdvancedOn) {
477 Bid.Trace("<prov.DbConnectionFactory.PruneConnectionPoolGroups|RES|INFO|CPOOL> %d#\n", ObjectID);
480 // First, walk the pool release list and attempt to clear each
481 // pool, when the pool is finally empty, we dispose of it. If the
482 // pool isn't empty, it's because there are active connections or
483 // distributed transactions that need it.
484 lock (_poolsToRelease) {
485 if (0 != _poolsToRelease.Count) {
486 DbConnectionPool[] poolsToRelease = _poolsToRelease.ToArray();
487 foreach (DbConnectionPool pool in poolsToRelease) {
491 if (0 == pool.Count) {
492 _poolsToRelease.Remove(pool);
493 if (Bid.AdvancedOn) {
494 Bid.Trace("<prov.DbConnectionFactory.PruneConnectionPoolGroups|RES|INFO|CPOOL> %d#, ReleasePool=%d#\n", ObjectID, pool.ObjectID);
497 PerformanceCounters.NumberOfInactiveConnectionPools.Decrement();
505 // Next, walk the pool entry release list and dispose of each
506 // pool entry when it is finally empty. If the pool entry isn't
507 // empty, it's because there are active pools that need it.
508 lock (_poolGroupsToRelease) {
509 if (0 != _poolGroupsToRelease.Count) {
510 DbConnectionPoolGroup[] poolGroupsToRelease = _poolGroupsToRelease.ToArray();
511 foreach (DbConnectionPoolGroup poolGroup in poolGroupsToRelease) {
512 if (null != poolGroup) {
513 int poolsLeft = poolGroup.Clear(); // may add entries to _poolsToRelease
515 if (0 == poolsLeft) {
516 _poolGroupsToRelease.Remove(poolGroup);
517 if (Bid.AdvancedOn) {
518 Bid.Trace("<prov.DbConnectionFactory.PruneConnectionPoolGroups|RES|INFO|CPOOL> %d#, ReleasePoolGroup=%d#\n", ObjectID, poolGroup.ObjectID);
521 PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Decrement();
529 // Finally, we walk through the collection of connection pool entries
530 // and prune each one. This will cause any empty pools to be put
531 // into the release list.
533 Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> connectionPoolGroups = _connectionPoolGroups;
534 Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup> newConnectionPoolGroups = new Dictionary<DbConnectionPoolKey, DbConnectionPoolGroup>(connectionPoolGroups.Count);
536 foreach (KeyValuePair<DbConnectionPoolKey, DbConnectionPoolGroup> entry in connectionPoolGroups) {
537 if (null != entry.Value) {
538 Debug.Assert(!entry.Value.IsDisabled, "Disabled pool entry discovered");
540 // entries start active and go idle during prune if all pools are gone
541 // move idle entries from last prune pass to a queue for pending release
542 // otherwise process entry which may move it from active to idle
543 if (entry.Value.Prune()) { // may add entries to _poolsToRelease
545 PerformanceCounters.NumberOfActiveConnectionPoolGroups.Decrement();
547 QueuePoolGroupForRelease(entry.Value);
550 newConnectionPoolGroups.Add(entry.Key, entry.Value);
554 _connectionPoolGroups = newConnectionPoolGroups;
558 internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) {
559 // Queue the pool up for release -- we'll clear it out and dispose
560 // of it as the last part of the pruning timer callback so we don't
561 // do it with the pool entry or the pool collection locked.
562 Debug.Assert (null != pool, "null pool?");
564 // set the pool to the shutdown state to force all active
565 // connections to be automatically disposed when they
566 // are returned to the pool
569 lock (_poolsToRelease) {
573 _poolsToRelease.Add(pool);
576 PerformanceCounters.NumberOfInactiveConnectionPools.Increment();
580 internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) {
581 Debug.Assert (null != poolGroup, "null poolGroup?");
582 Bid.Trace("<prov.DbConnectionFactory.QueuePoolGroupForRelease|RES|INFO|CPOOL> %d#, poolGroup=%d#\n", ObjectID, poolGroup.ObjectID);
584 lock (_poolGroupsToRelease) {
585 _poolGroupsToRelease.Add(poolGroup);
588 PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Increment();
592 virtual protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) {
593 return CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningConnection);
596 abstract protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection);
598 abstract protected DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous);
600 abstract protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(DbConnectionOptions options);
602 abstract internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection);
604 abstract internal DbConnectionInternal GetInnerConnection(DbConnection connection);
606 abstract protected int GetObjectId(DbConnection connection);
608 abstract internal void PermissionDemand(DbConnection outerConnection);
610 abstract internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup);
612 abstract internal void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to);
614 abstract internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from) ;
616 abstract internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to);