DbConnectionFactory.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Data / System / Data / ProviderBase / DbConnectionFactory.cs / 1 / DbConnectionFactory.cs

                            //------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Data.ProviderBase { 

    using System; 
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Data.Common;
    using System.Threading; 
#if ORACLE
    using System.Data.OracleClient; 
#endif 

    internal abstract class DbConnectionFactory { 
        private Dictionary  _connectionPoolGroups;
        private readonly List _poolsToRelease;
        private readonly List _poolGroupsToRelease;
        private readonly DbConnectionPoolCounters _performanceCounters; 
        private readonly Timer _pruningTimer;
 
        private const int PruningDueTime =4*60*1000;           // 4 minutes 
        private const int PruningPeriod  =  30*1000;           // thirty seconds
 
        private static int _objectTypeCount; // Bid counter
        internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);

        protected DbConnectionFactory() : this (DbConnectionPoolCountersNoCounters.SingletonInstance) { } 

        protected DbConnectionFactory(DbConnectionPoolCounters performanceCounters) { 
            _performanceCounters = performanceCounters; 
            _connectionPoolGroups = new Dictionary();
            _poolsToRelease = new List(); 
            _poolGroupsToRelease = new List();
            _pruningTimer = CreatePruningTimer();
        }
 
        internal DbConnectionPoolCounters PerformanceCounters {
            get { return _performanceCounters; } 
        } 

        abstract public DbProviderFactory ProviderFactory { 
            get;
        }

        internal int ObjectID { 
            get {
                return _objectID; 
            } 
        }
 
        public void ClearAllPools() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, " ");
            try { 
                Dictionary connectionPoolGroups = _connectionPoolGroups;
                foreach (KeyValuePair entry in connectionPoolGroups) { 
                    DbConnectionPoolGroup poolGroup = entry.Value; 
                    if (null != poolGroup) {
                        Debug.Assert(!poolGroup.IsDisabled, "Disabled pool entry discovered"); 
                        poolGroup.Clear();
                    }
                }
            } 
            finally {
                Bid.ScopeLeave(ref hscp); 
            } 
        }
 
        public void ClearPool(DbConnection connection) {
            ADP.CheckArgumentNull(connection, "connection");

            IntPtr hscp; 
            Bid.ScopeEnter(out hscp, " %d#" , GetObjectId(connection));
            try { 
                DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection); 
                if (null != poolGroup) {
                    poolGroup.Clear(); 
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp); 
            }
        } 
 
#if !ORACLE
        public void ClearPool(string connectionString) { 
            ADP.CheckArgumentNull(connectionString, "connectionString");

            IntPtr hscp;
            Bid.ScopeEnter(out hscp, " connectionString"); 
            try {
                DbConnectionPoolGroup poolGroup; 
                Dictionary connectionPoolGroups = _connectionPoolGroups; 
                if (connectionPoolGroups.TryGetValue(connectionString, out poolGroup)) {
                    poolGroup.Clear(); 
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp); 
            }
        } 
#endif 

        internal virtual DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions){ 
            return null;
        }

        virtual protected DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory) { 
            // providers that support GetSchema must override this with a method that creates a meta data
            // factory appropriate for them. 
            cacheMetaDataFactory = false; 
            throw ADP.NotSupported();
        } 

        internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup) {
            Debug.Assert(null != owningConnection, "null owningConnection?");
            Debug.Assert(null != poolGroup, "null poolGroup?"); 

            DbConnectionOptions connectionOptions = poolGroup.ConnectionOptions; 
            DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = poolGroup.ProviderInfo; 

            DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolGroupProviderInfo, null, owningConnection); 
            if (null != newConnection) {
                PerformanceCounters.HardConnectsPerSecond.Increment();
                newConnection.MakeNonPooledObject(owningConnection, PerformanceCounters);
            } 
            Bid.Trace(" %d#, Non-pooled database connection created.\n", ObjectID);
            return newConnection; 
        } 

        internal DbConnectionInternal CreatePooledConnection(DbConnection owningConnection, DbConnectionPool pool, DbConnectionOptions options) { 
            Debug.Assert(null != pool, "null pool?");
            DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = pool.PoolGroup.ProviderInfo;

            DbConnectionInternal newConnection = CreateConnection(options, poolGroupProviderInfo, pool, owningConnection); 
            if (null != newConnection) {
                PerformanceCounters.HardConnectsPerSecond.Increment(); 
                newConnection.MakePooledConnection(pool); 
            }
            Bid.Trace(" %d#, Pooled database connection created.\n", ObjectID); 
            return newConnection;
        }

        virtual internal DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo (DbConnectionOptions connectionOptions) { 
            return null;
        } 
 
        private Timer CreatePruningTimer() {
            TimerCallback callback = new TimerCallback(PruneConnectionPoolGroups); 
            return new Timer(callback, null, PruningDueTime, PruningPeriod);
        }

#if !ORACLE 
        protected DbConnectionOptions FindConnectionOptions(string connectionString) {
            if (!ADP.IsEmpty(connectionString)) { 
                DbConnectionPoolGroup connectionPoolGroup; 
                Dictionary connectionPoolGroups = _connectionPoolGroups;
                if (connectionPoolGroups.TryGetValue(connectionString, out connectionPoolGroup)) { 
                    return connectionPoolGroup.ConnectionOptions;
                }
            }
            return null; 
        }
#endif 
 
        internal DbConnectionInternal GetConnection(DbConnection owningConnection) {
            Debug.Assert(null != owningConnection, "null owningConnection?"); 

            DbConnectionPoolGroup poolGroup;
            DbConnectionPool connectionPool;
            DbConnectionInternal connection; 

            // SQLBU 431251: 
            //  Work around race condition with clearing the pool between GetConnectionPool obtaining pool 
            //  and GetConnection on the pool checking the pool state.  Clearing the pool in this window
            //  will switch the pool into the ShuttingDown state, and GetConnection will return null. 
            //  There is probably a better solution involving locking the pool/group, but that entails a major
            //  re-design of the connection pooling synchronization, so is post-poned for now.

            // use retriesLeft to prevent CPU spikes 
            int retriesLeft = 5;
 
            do { 
                poolGroup = GetConnectionPoolGroup(owningConnection);
                connectionPool = GetConnectionPool(owningConnection, poolGroup); 
                if (null == connectionPool) {
                    // If GetConnectionPool returns null, we can be certain that
                    // this connection should not be pooled via DbConnectionPool
                    // or have a disabled pool entry. 
                    poolGroup = GetConnectionPoolGroup(owningConnection); // previous entry have been disabled
                    connection = CreateNonPooledConnection(owningConnection, poolGroup); 
                    PerformanceCounters.NumberOfNonPooledConnections.Increment(); 
                }
                else { 
                    connection = connectionPool.GetConnection(owningConnection);

                    if (connection != null) {
                        // we are done 
                        break;
                    } 
 
                    // connection creation failed on semaphore waiting or if max pool reached
                    if (connectionPool.IsRunning) { 
                        // If GetConnection failed while the pool is running, the pool timeout occurred.
                        Bid.Trace(" %d#, GetConnection failed because a pool timeout occurred.\n", ObjectID);
                        throw ADP.PooledOpenTimeout();
                    } 
                    else {
                        // We've hit the race condition, where the pool was shut down after we got it from the group. 
                        // Yield time slice to allow shut down activities to complete and a new, running pool to be instantiated 
                        //  before retrying.
                        Threading.Thread.Sleep(1); 
                    }
                }
            } while (connection == null && (retriesLeft--) > 0);
 
            if (connection == null) {
                // exhausted all retries or timed out - give up 
                Bid.Trace(" %d#, GetConnection failed because a pool timeout occurred and all retries were exhausted.\n", ObjectID); 
                throw ADP.PooledOpenTimeout();
            } 

            return connection;
        }
 
        private DbConnectionPool GetConnectionPool(DbConnection owningObject, DbConnectionPoolGroup connectionPoolGroup) {
            // if poolgroup is disabled, it will be replaced with a new entry 
 
            Debug.Assert(null != owningObject, "null owningObject?");
            Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?"); 

            // It is possible that while the outer connection object has
            // been sitting around in a closed and unused state in some long
            // running app, the pruner may have come along and remove this 
            // the pool entry from the master list.  If we were to use a
            // pool entry in this state, we would create "unmanaged" pools, 
            // which would be bad.  To avoid this problem, we automagically 
            // re-create the pool entry whenever it's disabled.
 
            // however, don't rebuild connectionOptions if no pooling is involved - let new connections do that work
            if (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions)) {
                Bid.Trace(" %d#, DisabledPoolGroup=%d#\n", ObjectID, connectionPoolGroup.ObjectID);
 
                // reusing existing pool option in case user originally used SetConnectionPoolOptions
                DbConnectionPoolGroupOptions poolOptions = connectionPoolGroup.PoolGroupOptions; 
 
                // get the string to hash on again
                DbConnectionOptions connectionOptions = connectionPoolGroup.ConnectionOptions; 
                string connectionString = connectionOptions.UsersConnectionString(false);

                Debug.Assert(null != connectionOptions, "prevent expansion of connectionString");
                connectionPoolGroup = GetConnectionPoolGroup(connectionString, poolOptions, ref connectionOptions); 
                Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?");
                SetConnectionPoolGroup(owningObject, connectionPoolGroup); 
            } 
            DbConnectionPool connectionPool = connectionPoolGroup.GetConnectionPool(this);
            return connectionPool; 
        }

        internal DbConnectionPoolGroup GetConnectionPoolGroup(string connectionString,  DbConnectionPoolGroupOptions poolOptions, ref DbConnectionOptions userConnectionOptions) {
            if (ADP.IsEmpty(connectionString)) { 
                return (DbConnectionPoolGroup)null;
            } 
 
            DbConnectionPoolGroup connectionPoolGroup;
            Dictionary connectionPoolGroups = _connectionPoolGroups; 
            if (!connectionPoolGroups.TryGetValue(connectionString, out connectionPoolGroup) || (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions))) {
                // If we can't find an entry for the connection string in
                // our collection of pool entries, then we need to create a
                // new pool entry and add it to our collection. 

                DbConnectionOptions connectionOptions = CreateConnectionOptions(connectionString, userConnectionOptions); 
                if (null == connectionOptions) { 
                    throw ADP.InternalConnectionError(ADP.ConnectionError.ConnectionOptionsMissing);
                } 

                string expandedConnectionString = connectionString;
                if (null == userConnectionOptions) { // we only allow one expansion on the connection string
 
                    userConnectionOptions = connectionOptions;
                    expandedConnectionString = connectionOptions.Expand(); 
 
                    // if the expanded string is same instance (default implementation), the use the already created options
                    if ((object)expandedConnectionString != (object)connectionString) { 
                        //
                        return GetConnectionPoolGroup(expandedConnectionString, null, ref userConnectionOptions);
                    }
                } 

                // We don't support connection pooling on Win9x; it lacks too many of the APIs we require. 
                if ((null == poolOptions) && ADP.IsWindowsNT) { 
                    if (null != connectionPoolGroup) {
                        // reusing existing pool option in case user originally used SetConnectionPoolOptions 
                        poolOptions = connectionPoolGroup.PoolGroupOptions;
                    }
                    else {
                        // Note: may return null for non-pooled connections 
                        poolOptions = CreateConnectionPoolGroupOptions(connectionOptions);
                    } 
                } 

 
                DbConnectionPoolGroup newConnectionPoolGroup = new DbConnectionPoolGroup(connectionOptions, poolOptions);
                newConnectionPoolGroup.ProviderInfo = CreateConnectionPoolGroupProviderInfo(connectionOptions);

                lock (this) { 
                    connectionPoolGroups = _connectionPoolGroups;
                    if (!connectionPoolGroups.TryGetValue(expandedConnectionString, out connectionPoolGroup)) { 
                        // build new dictionary with space for new connection string 
                        Dictionary newConnectionPoolGroups = new Dictionary(1+connectionPoolGroups.Count);
                        foreach (KeyValuePair entry in connectionPoolGroups) { 
                            newConnectionPoolGroups.Add(entry.Key, entry.Value);
                        }

                        // lock prevents race condition with PruneConnectionPoolGroups 
                        newConnectionPoolGroups.Add(expandedConnectionString, newConnectionPoolGroup);
                        PerformanceCounters.NumberOfActiveConnectionPoolGroups.Increment(); 
                        connectionPoolGroup = newConnectionPoolGroup; 
                        _connectionPoolGroups = newConnectionPoolGroups;
                    } 
                    else {
                        Debug.Assert(!connectionPoolGroup.IsDisabled, "Disabled pool entry discovered");
                    }
                } 
                Debug.Assert(null != connectionPoolGroup, "how did we not create a pool entry?");
                Debug.Assert(null != userConnectionOptions, "how did we not have user connection options?"); 
            } 
            else if (null == userConnectionOptions) {
                userConnectionOptions = connectionPoolGroup.ConnectionOptions; 
            }
            return connectionPoolGroup;
        }
 
        internal DbMetaDataFactory GetMetaDataFactory(DbConnectionPoolGroup connectionPoolGroup,DbConnectionInternal internalConnection){
            Debug.Assert (connectionPoolGroup != null, "connectionPoolGroup may not be null."); 
 
            // get the matadatafactory from the pool entry. If it does not already have one
            // create one and save it on the pool entry 
            DbMetaDataFactory metaDataFactory = connectionPoolGroup.MetaDataFactory;

            // consider serializing this so we don't construct multiple metadata factories
            // if two threads happen to hit this at the same time.  One will be GC'd 
            if (metaDataFactory == null){
                bool allowCache = false; 
                metaDataFactory = CreateMetaDataFactory(internalConnection, out allowCache); 
                if (allowCache) {
                    connectionPoolGroup.MetaDataFactory = metaDataFactory; 
                }
            }
            return metaDataFactory;
        } 

        private void PruneConnectionPoolGroups(object state) { 
            // when debugging this method, expect multiple threads at the same time 
            if (Bid.AdvancedOn) {
                Bid.Trace(" %d#\n", ObjectID); 
            }

            // First, walk the pool release list and attempt to clear each
            // pool, when the pool is finally empty, we dispose of it.  If the 
            // pool isn't empty, it's because there are active connections or
            // distributed transactions that need it. 
            lock (_poolsToRelease) { 
                if (0 != _poolsToRelease.Count) {
                    DbConnectionPool[] poolsToRelease = _poolsToRelease.ToArray(); 
                    foreach (DbConnectionPool pool in poolsToRelease) {
                        if (null != pool) {
                            pool.Clear();
 
                            if (0 == pool.Count) {
                                _poolsToRelease.Remove(pool); 
                                if (Bid.AdvancedOn) { 
                                    Bid.Trace(" %d#, ReleasePool=%d#\n", ObjectID, pool.ObjectID);
                                } 
                                PerformanceCounters.NumberOfInactiveConnectionPools.Decrement();
                            }
                        }
                    } 
                }
            } 
 
            // Next, walk the pool entry release list and dispose of each
            // pool entry when it is finally empty.  If the pool entry isn't 
            // empty, it's because there are active pools that need it.
            lock (_poolGroupsToRelease) {
                if (0 != _poolGroupsToRelease.Count) {
                    DbConnectionPoolGroup[] poolGroupsToRelease = _poolGroupsToRelease.ToArray(); 
                    foreach (DbConnectionPoolGroup poolGroup in poolGroupsToRelease) {
                        if (null != poolGroup) { 
                            poolGroup.Clear(); // may add entries to _poolsToRelease 

                            if (0 == poolGroup.Count) { 
                                _poolGroupsToRelease.Remove(poolGroup);
                                if (Bid.AdvancedOn) {
                                    Bid.Trace(" %d#, ReleasePoolGroup=%d#\n", ObjectID, poolGroup.ObjectID);
                                } 
                                PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Decrement();
                            } 
                        } 
                    }
                } 
            }

            // Finally, we walk through the collection of connection pool entries
            // and prune each one.  This will cause any empty pools to be put 
            // into the release list.
            lock (this) { 
                Dictionary connectionPoolGroups = _connectionPoolGroups; 
                Dictionary newConnectionPoolGroups = new Dictionary(connectionPoolGroups.Count);
 
                foreach (KeyValuePair entry in connectionPoolGroups) {
                    if (null != entry.Value) {
                        Debug.Assert(!entry.Value.IsDisabled, "Disabled pool entry discovered");
 
                        // entries start active and go idle during prune if all pools are gone
                        // move idle entries from last prune pass to a queue for pending release 
                        // otherwise process entry which may move it from active to idle 
                        if (entry.Value.Prune()) { // may add entries to _poolsToRelease
                            PerformanceCounters.NumberOfActiveConnectionPoolGroups.Decrement(); 
                            QueuePoolGroupForRelease(entry.Value);
                        }
                        else {
                            newConnectionPoolGroups.Add(entry.Key, entry.Value); 
                        }
                    } 
                } 
                _connectionPoolGroups = newConnectionPoolGroups;
            } 
        }

        internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) {
            // Queue the pool up for release -- we'll clear it out and dispose 
            // of it as the last part of the pruning timer callback so we don't
            // do it with the pool entry or the pool collection locked. 
            Debug.Assert (null != pool, "null pool?"); 

            // set the pool to the shutdown state to force all active 
            // connections to be automatically disposed when they
            // are returned to the pool
            pool.Shutdown();
 
            lock (_poolsToRelease) {
                if (clearing) { 
                    pool.Clear(); 
                }
                _poolsToRelease.Add(pool); 
            }
            PerformanceCounters.NumberOfInactiveConnectionPools.Increment();
        }
 
        internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) {
            Debug.Assert (null != poolGroup, "null poolGroup?"); 
            Bid.Trace(" %d#, poolGroup=%d#\n", ObjectID, poolGroup.ObjectID); 

            lock (_poolGroupsToRelease) { 
                _poolGroupsToRelease.Add(poolGroup);
            }
            PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Increment();
        } 

        abstract protected DbConnectionInternal CreateConnection(DbConnectionOptions options, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection); 
 
        abstract protected DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous);
 
        abstract protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(DbConnectionOptions options);

        abstract internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection);
 
        abstract internal DbConnectionInternal GetInnerConnection(DbConnection connection);
 
        abstract protected int GetObjectId(DbConnection connection); 

        abstract internal void PermissionDemand(DbConnection outerConnection); 

        abstract internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup);

        abstract internal void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to); 

        abstract internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from) ; 
 
        abstract internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to);
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Data.ProviderBase { 

    using System; 
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Data.Common;
    using System.Threading; 
#if ORACLE
    using System.Data.OracleClient; 
#endif 

    internal abstract class DbConnectionFactory { 
        private Dictionary  _connectionPoolGroups;
        private readonly List _poolsToRelease;
        private readonly List _poolGroupsToRelease;
        private readonly DbConnectionPoolCounters _performanceCounters; 
        private readonly Timer _pruningTimer;
 
        private const int PruningDueTime =4*60*1000;           // 4 minutes 
        private const int PruningPeriod  =  30*1000;           // thirty seconds
 
        private static int _objectTypeCount; // Bid counter
        internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);

        protected DbConnectionFactory() : this (DbConnectionPoolCountersNoCounters.SingletonInstance) { } 

        protected DbConnectionFactory(DbConnectionPoolCounters performanceCounters) { 
            _performanceCounters = performanceCounters; 
            _connectionPoolGroups = new Dictionary();
            _poolsToRelease = new List(); 
            _poolGroupsToRelease = new List();
            _pruningTimer = CreatePruningTimer();
        }
 
        internal DbConnectionPoolCounters PerformanceCounters {
            get { return _performanceCounters; } 
        } 

        abstract public DbProviderFactory ProviderFactory { 
            get;
        }

        internal int ObjectID { 
            get {
                return _objectID; 
            } 
        }
 
        public void ClearAllPools() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, " ");
            try { 
                Dictionary connectionPoolGroups = _connectionPoolGroups;
                foreach (KeyValuePair entry in connectionPoolGroups) { 
                    DbConnectionPoolGroup poolGroup = entry.Value; 
                    if (null != poolGroup) {
                        Debug.Assert(!poolGroup.IsDisabled, "Disabled pool entry discovered"); 
                        poolGroup.Clear();
                    }
                }
            } 
            finally {
                Bid.ScopeLeave(ref hscp); 
            } 
        }
 
        public void ClearPool(DbConnection connection) {
            ADP.CheckArgumentNull(connection, "connection");

            IntPtr hscp; 
            Bid.ScopeEnter(out hscp, " %d#" , GetObjectId(connection));
            try { 
                DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection); 
                if (null != poolGroup) {
                    poolGroup.Clear(); 
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp); 
            }
        } 
 
#if !ORACLE
        public void ClearPool(string connectionString) { 
            ADP.CheckArgumentNull(connectionString, "connectionString");

            IntPtr hscp;
            Bid.ScopeEnter(out hscp, " connectionString"); 
            try {
                DbConnectionPoolGroup poolGroup; 
                Dictionary connectionPoolGroups = _connectionPoolGroups; 
                if (connectionPoolGroups.TryGetValue(connectionString, out poolGroup)) {
                    poolGroup.Clear(); 
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp); 
            }
        } 
#endif 

        internal virtual DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions){ 
            return null;
        }

        virtual protected DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory) { 
            // providers that support GetSchema must override this with a method that creates a meta data
            // factory appropriate for them. 
            cacheMetaDataFactory = false; 
            throw ADP.NotSupported();
        } 

        internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup) {
            Debug.Assert(null != owningConnection, "null owningConnection?");
            Debug.Assert(null != poolGroup, "null poolGroup?"); 

            DbConnectionOptions connectionOptions = poolGroup.ConnectionOptions; 
            DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = poolGroup.ProviderInfo; 

            DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolGroupProviderInfo, null, owningConnection); 
            if (null != newConnection) {
                PerformanceCounters.HardConnectsPerSecond.Increment();
                newConnection.MakeNonPooledObject(owningConnection, PerformanceCounters);
            } 
            Bid.Trace(" %d#, Non-pooled database connection created.\n", ObjectID);
            return newConnection; 
        } 

        internal DbConnectionInternal CreatePooledConnection(DbConnection owningConnection, DbConnectionPool pool, DbConnectionOptions options) { 
            Debug.Assert(null != pool, "null pool?");
            DbConnectionPoolGroupProviderInfo poolGroupProviderInfo = pool.PoolGroup.ProviderInfo;

            DbConnectionInternal newConnection = CreateConnection(options, poolGroupProviderInfo, pool, owningConnection); 
            if (null != newConnection) {
                PerformanceCounters.HardConnectsPerSecond.Increment(); 
                newConnection.MakePooledConnection(pool); 
            }
            Bid.Trace(" %d#, Pooled database connection created.\n", ObjectID); 
            return newConnection;
        }

        virtual internal DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo (DbConnectionOptions connectionOptions) { 
            return null;
        } 
 
        private Timer CreatePruningTimer() {
            TimerCallback callback = new TimerCallback(PruneConnectionPoolGroups); 
            return new Timer(callback, null, PruningDueTime, PruningPeriod);
        }

#if !ORACLE 
        protected DbConnectionOptions FindConnectionOptions(string connectionString) {
            if (!ADP.IsEmpty(connectionString)) { 
                DbConnectionPoolGroup connectionPoolGroup; 
                Dictionary connectionPoolGroups = _connectionPoolGroups;
                if (connectionPoolGroups.TryGetValue(connectionString, out connectionPoolGroup)) { 
                    return connectionPoolGroup.ConnectionOptions;
                }
            }
            return null; 
        }
#endif 
 
        internal DbConnectionInternal GetConnection(DbConnection owningConnection) {
            Debug.Assert(null != owningConnection, "null owningConnection?"); 

            DbConnectionPoolGroup poolGroup;
            DbConnectionPool connectionPool;
            DbConnectionInternal connection; 

            // SQLBU 431251: 
            //  Work around race condition with clearing the pool between GetConnectionPool obtaining pool 
            //  and GetConnection on the pool checking the pool state.  Clearing the pool in this window
            //  will switch the pool into the ShuttingDown state, and GetConnection will return null. 
            //  There is probably a better solution involving locking the pool/group, but that entails a major
            //  re-design of the connection pooling synchronization, so is post-poned for now.

            // use retriesLeft to prevent CPU spikes 
            int retriesLeft = 5;
 
            do { 
                poolGroup = GetConnectionPoolGroup(owningConnection);
                connectionPool = GetConnectionPool(owningConnection, poolGroup); 
                if (null == connectionPool) {
                    // If GetConnectionPool returns null, we can be certain that
                    // this connection should not be pooled via DbConnectionPool
                    // or have a disabled pool entry. 
                    poolGroup = GetConnectionPoolGroup(owningConnection); // previous entry have been disabled
                    connection = CreateNonPooledConnection(owningConnection, poolGroup); 
                    PerformanceCounters.NumberOfNonPooledConnections.Increment(); 
                }
                else { 
                    connection = connectionPool.GetConnection(owningConnection);

                    if (connection != null) {
                        // we are done 
                        break;
                    } 
 
                    // connection creation failed on semaphore waiting or if max pool reached
                    if (connectionPool.IsRunning) { 
                        // If GetConnection failed while the pool is running, the pool timeout occurred.
                        Bid.Trace(" %d#, GetConnection failed because a pool timeout occurred.\n", ObjectID);
                        throw ADP.PooledOpenTimeout();
                    } 
                    else {
                        // We've hit the race condition, where the pool was shut down after we got it from the group. 
                        // Yield time slice to allow shut down activities to complete and a new, running pool to be instantiated 
                        //  before retrying.
                        Threading.Thread.Sleep(1); 
                    }
                }
            } while (connection == null && (retriesLeft--) > 0);
 
            if (connection == null) {
                // exhausted all retries or timed out - give up 
                Bid.Trace(" %d#, GetConnection failed because a pool timeout occurred and all retries were exhausted.\n", ObjectID); 
                throw ADP.PooledOpenTimeout();
            } 

            return connection;
        }
 
        private DbConnectionPool GetConnectionPool(DbConnection owningObject, DbConnectionPoolGroup connectionPoolGroup) {
            // if poolgroup is disabled, it will be replaced with a new entry 
 
            Debug.Assert(null != owningObject, "null owningObject?");
            Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?"); 

            // It is possible that while the outer connection object has
            // been sitting around in a closed and unused state in some long
            // running app, the pruner may have come along and remove this 
            // the pool entry from the master list.  If we were to use a
            // pool entry in this state, we would create "unmanaged" pools, 
            // which would be bad.  To avoid this problem, we automagically 
            // re-create the pool entry whenever it's disabled.
 
            // however, don't rebuild connectionOptions if no pooling is involved - let new connections do that work
            if (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions)) {
                Bid.Trace(" %d#, DisabledPoolGroup=%d#\n", ObjectID, connectionPoolGroup.ObjectID);
 
                // reusing existing pool option in case user originally used SetConnectionPoolOptions
                DbConnectionPoolGroupOptions poolOptions = connectionPoolGroup.PoolGroupOptions; 
 
                // get the string to hash on again
                DbConnectionOptions connectionOptions = connectionPoolGroup.ConnectionOptions; 
                string connectionString = connectionOptions.UsersConnectionString(false);

                Debug.Assert(null != connectionOptions, "prevent expansion of connectionString");
                connectionPoolGroup = GetConnectionPoolGroup(connectionString, poolOptions, ref connectionOptions); 
                Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup?");
                SetConnectionPoolGroup(owningObject, connectionPoolGroup); 
            } 
            DbConnectionPool connectionPool = connectionPoolGroup.GetConnectionPool(this);
            return connectionPool; 
        }

        internal DbConnectionPoolGroup GetConnectionPoolGroup(string connectionString,  DbConnectionPoolGroupOptions poolOptions, ref DbConnectionOptions userConnectionOptions) {
            if (ADP.IsEmpty(connectionString)) { 
                return (DbConnectionPoolGroup)null;
            } 
 
            DbConnectionPoolGroup connectionPoolGroup;
            Dictionary connectionPoolGroups = _connectionPoolGroups; 
            if (!connectionPoolGroups.TryGetValue(connectionString, out connectionPoolGroup) || (connectionPoolGroup.IsDisabled && (null != connectionPoolGroup.PoolGroupOptions))) {
                // If we can't find an entry for the connection string in
                // our collection of pool entries, then we need to create a
                // new pool entry and add it to our collection. 

                DbConnectionOptions connectionOptions = CreateConnectionOptions(connectionString, userConnectionOptions); 
                if (null == connectionOptions) { 
                    throw ADP.InternalConnectionError(ADP.ConnectionError.ConnectionOptionsMissing);
                } 

                string expandedConnectionString = connectionString;
                if (null == userConnectionOptions) { // we only allow one expansion on the connection string
 
                    userConnectionOptions = connectionOptions;
                    expandedConnectionString = connectionOptions.Expand(); 
 
                    // if the expanded string is same instance (default implementation), the use the already created options
                    if ((object)expandedConnectionString != (object)connectionString) { 
                        //
                        return GetConnectionPoolGroup(expandedConnectionString, null, ref userConnectionOptions);
                    }
                } 

                // We don't support connection pooling on Win9x; it lacks too many of the APIs we require. 
                if ((null == poolOptions) && ADP.IsWindowsNT) { 
                    if (null != connectionPoolGroup) {
                        // reusing existing pool option in case user originally used SetConnectionPoolOptions 
                        poolOptions = connectionPoolGroup.PoolGroupOptions;
                    }
                    else {
                        // Note: may return null for non-pooled connections 
                        poolOptions = CreateConnectionPoolGroupOptions(connectionOptions);
                    } 
                } 

 
                DbConnectionPoolGroup newConnectionPoolGroup = new DbConnectionPoolGroup(connectionOptions, poolOptions);
                newConnectionPoolGroup.ProviderInfo = CreateConnectionPoolGroupProviderInfo(connectionOptions);

                lock (this) { 
                    connectionPoolGroups = _connectionPoolGroups;
                    if (!connectionPoolGroups.TryGetValue(expandedConnectionString, out connectionPoolGroup)) { 
                        // build new dictionary with space for new connection string 
                        Dictionary newConnectionPoolGroups = new Dictionary(1+connectionPoolGroups.Count);
                        foreach (KeyValuePair entry in connectionPoolGroups) { 
                            newConnectionPoolGroups.Add(entry.Key, entry.Value);
                        }

                        // lock prevents race condition with PruneConnectionPoolGroups 
                        newConnectionPoolGroups.Add(expandedConnectionString, newConnectionPoolGroup);
                        PerformanceCounters.NumberOfActiveConnectionPoolGroups.Increment(); 
                        connectionPoolGroup = newConnectionPoolGroup; 
                        _connectionPoolGroups = newConnectionPoolGroups;
                    } 
                    else {
                        Debug.Assert(!connectionPoolGroup.IsDisabled, "Disabled pool entry discovered");
                    }
                } 
                Debug.Assert(null != connectionPoolGroup, "how did we not create a pool entry?");
                Debug.Assert(null != userConnectionOptions, "how did we not have user connection options?"); 
            } 
            else if (null == userConnectionOptions) {
                userConnectionOptions = connectionPoolGroup.ConnectionOptions; 
            }
            return connectionPoolGroup;
        }
 
        internal DbMetaDataFactory GetMetaDataFactory(DbConnectionPoolGroup connectionPoolGroup,DbConnectionInternal internalConnection){
            Debug.Assert (connectionPoolGroup != null, "connectionPoolGroup may not be null."); 
 
            // get the matadatafactory from the pool entry. If it does not already have one
            // create one and save it on the pool entry 
            DbMetaDataFactory metaDataFactory = connectionPoolGroup.MetaDataFactory;

            // consider serializing this so we don't construct multiple metadata factories
            // if two threads happen to hit this at the same time.  One will be GC'd 
            if (metaDataFactory == null){
                bool allowCache = false; 
                metaDataFactory = CreateMetaDataFactory(internalConnection, out allowCache); 
                if (allowCache) {
                    connectionPoolGroup.MetaDataFactory = metaDataFactory; 
                }
            }
            return metaDataFactory;
        } 

        private void PruneConnectionPoolGroups(object state) { 
            // when debugging this method, expect multiple threads at the same time 
            if (Bid.AdvancedOn) {
                Bid.Trace(" %d#\n", ObjectID); 
            }

            // First, walk the pool release list and attempt to clear each
            // pool, when the pool is finally empty, we dispose of it.  If the 
            // pool isn't empty, it's because there are active connections or
            // distributed transactions that need it. 
            lock (_poolsToRelease) { 
                if (0 != _poolsToRelease.Count) {
                    DbConnectionPool[] poolsToRelease = _poolsToRelease.ToArray(); 
                    foreach (DbConnectionPool pool in poolsToRelease) {
                        if (null != pool) {
                            pool.Clear();
 
                            if (0 == pool.Count) {
                                _poolsToRelease.Remove(pool); 
                                if (Bid.AdvancedOn) { 
                                    Bid.Trace(" %d#, ReleasePool=%d#\n", ObjectID, pool.ObjectID);
                                } 
                                PerformanceCounters.NumberOfInactiveConnectionPools.Decrement();
                            }
                        }
                    } 
                }
            } 
 
            // Next, walk the pool entry release list and dispose of each
            // pool entry when it is finally empty.  If the pool entry isn't 
            // empty, it's because there are active pools that need it.
            lock (_poolGroupsToRelease) {
                if (0 != _poolGroupsToRelease.Count) {
                    DbConnectionPoolGroup[] poolGroupsToRelease = _poolGroupsToRelease.ToArray(); 
                    foreach (DbConnectionPoolGroup poolGroup in poolGroupsToRelease) {
                        if (null != poolGroup) { 
                            poolGroup.Clear(); // may add entries to _poolsToRelease 

                            if (0 == poolGroup.Count) { 
                                _poolGroupsToRelease.Remove(poolGroup);
                                if (Bid.AdvancedOn) {
                                    Bid.Trace(" %d#, ReleasePoolGroup=%d#\n", ObjectID, poolGroup.ObjectID);
                                } 
                                PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Decrement();
                            } 
                        } 
                    }
                } 
            }

            // Finally, we walk through the collection of connection pool entries
            // and prune each one.  This will cause any empty pools to be put 
            // into the release list.
            lock (this) { 
                Dictionary connectionPoolGroups = _connectionPoolGroups; 
                Dictionary newConnectionPoolGroups = new Dictionary(connectionPoolGroups.Count);
 
                foreach (KeyValuePair entry in connectionPoolGroups) {
                    if (null != entry.Value) {
                        Debug.Assert(!entry.Value.IsDisabled, "Disabled pool entry discovered");
 
                        // entries start active and go idle during prune if all pools are gone
                        // move idle entries from last prune pass to a queue for pending release 
                        // otherwise process entry which may move it from active to idle 
                        if (entry.Value.Prune()) { // may add entries to _poolsToRelease
                            PerformanceCounters.NumberOfActiveConnectionPoolGroups.Decrement(); 
                            QueuePoolGroupForRelease(entry.Value);
                        }
                        else {
                            newConnectionPoolGroups.Add(entry.Key, entry.Value); 
                        }
                    } 
                } 
                _connectionPoolGroups = newConnectionPoolGroups;
            } 
        }

        internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) {
            // Queue the pool up for release -- we'll clear it out and dispose 
            // of it as the last part of the pruning timer callback so we don't
            // do it with the pool entry or the pool collection locked. 
            Debug.Assert (null != pool, "null pool?"); 

            // set the pool to the shutdown state to force all active 
            // connections to be automatically disposed when they
            // are returned to the pool
            pool.Shutdown();
 
            lock (_poolsToRelease) {
                if (clearing) { 
                    pool.Clear(); 
                }
                _poolsToRelease.Add(pool); 
            }
            PerformanceCounters.NumberOfInactiveConnectionPools.Increment();
        }
 
        internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) {
            Debug.Assert (null != poolGroup, "null poolGroup?"); 
            Bid.Trace(" %d#, poolGroup=%d#\n", ObjectID, poolGroup.ObjectID); 

            lock (_poolGroupsToRelease) { 
                _poolGroupsToRelease.Add(poolGroup);
            }
            PerformanceCounters.NumberOfInactiveConnectionPoolGroups.Increment();
        } 

        abstract protected DbConnectionInternal CreateConnection(DbConnectionOptions options, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection); 
 
        abstract protected DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous);
 
        abstract protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(DbConnectionOptions options);

        abstract internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection);
 
        abstract internal DbConnectionInternal GetInnerConnection(DbConnection connection);
 
        abstract protected int GetObjectId(DbConnection connection); 

        abstract internal void PermissionDemand(DbConnection outerConnection); 

        abstract internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup);

        abstract internal void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to); 

        abstract internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from) ; 
 
        abstract internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to);
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK