| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885 |
- //------------------------------------------------------------------------------
- // <copyright file="DbConnectionInternal.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data.ProviderBase {
- using System;
- using System.ComponentModel;
- using System.Data;
- using System.Data.Common;
- using System.Diagnostics;
- using System.Globalization;
- using System.Runtime.ConstrainedExecution;
- using System.Runtime.InteropServices;
- using System.Runtime.InteropServices.ComTypes;
- using System.Security;
- using System.Security.Permissions;
- using System.Threading;
- using System.Threading.Tasks;
- using SysTx = System.Transactions;
- internal abstract class DbConnectionInternal { // V1.1.3300
-
- private static int _objectTypeCount;
- internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount);
- internal static readonly StateChangeEventArgs StateChangeClosed = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed);
- internal static readonly StateChangeEventArgs StateChangeOpen = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open);
- private readonly bool _allowSetConnectionString;
- private readonly bool _hidePassword;
- private readonly ConnectionState _state;
- private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections)
- private DbConnectionPool _connectionPool; // the pooler that the connection came from (Pooled connections only)
- private DbConnectionPoolCounters _performanceCounters; // the performance counters we're supposed to update
- private DbReferenceCollection _referenceCollection; // collection of objects that we need to notify in some way when we're being deactivated
- private int _pooledCount; // [usage must be thread safe] the number of times this object has been pushed into the pool less the number of times it's been popped (0 != inPool)
- private bool _connectionIsDoomed; // true when the connection should no longer be used.
- private bool _cannotBePooled; // true when the connection should no longer be pooled.
- private bool _isInStasis;
- private DateTime _createTime; // when the connection was created.
- private SysTx.Transaction _enlistedTransaction; // [usage must be thread-safe] the transaction that we're enlisted in, either manually or automatically
- // _enlistedTransaction is a clone, so that transaction information can be queried even if the original transaction object is disposed.
- // However, there are times when we need to know if the original transaction object was disposed, so we keep a reference to it here.
- // This field should only be assigned a value at the same time _enlistedTransaction is updated.
- // Also, this reference should not be disposed, since we aren't taking ownership of it.
- private SysTx.Transaction _enlistedTransactionOriginal;
- #if DEBUG
- private int _activateCount; // debug only counter to verify activate/deactivates are in [....].
- #endif //DEBUG
- protected DbConnectionInternal() : this(ConnectionState.Open, true, false) { // V1.1.3300
- }
- // Constructor for internal connections
- internal DbConnectionInternal(ConnectionState state, bool hidePassword, bool allowSetConnectionString) {
- _allowSetConnectionString = allowSetConnectionString;
- _hidePassword = hidePassword;
- _state = state;
- }
- internal bool AllowSetConnectionString {
- get {
- return _allowSetConnectionString;
- }
- }
- internal bool CanBePooled {
- get {
- bool flag = (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.IsAlive);
- return flag;
- }
- }
- protected internal SysTx.Transaction EnlistedTransaction {
- get {
- return _enlistedTransaction;
- }
- set {
- SysTx.Transaction currentEnlistedTransaction = _enlistedTransaction;
- if (((null == currentEnlistedTransaction) && (null != value))
- || ((null != currentEnlistedTransaction) && !currentEnlistedTransaction.Equals(value))) { // WebData 20000024
- // Pay attention to the order here:
- // 1) defect from any notifications
- // 2) replace the transaction
- // 3) re-enlist in notifications for the new transaction
- // SQLBUDT #230558 we need to use a clone of the transaction
- // when we store it, or we'll end up keeping it past the
- // duration of the using block of the TransactionScope
- SysTx.Transaction valueClone = null;
- SysTx.Transaction previousTransactionClone = null;
- try {
- if (null != value) {
- valueClone = value.Clone();
- }
- // NOTE: rather than take locks around several potential round-
- // trips to the server, and/or virtual function calls, we simply
- // presume that you aren't doing something illegal from multiple
- // threads, and check once we get around to finalizing things
- // inside a lock.
- lock(this) {
- // NOTE: There is still a race condition here, when we are
- // called from EnlistTransaction (which cannot re-enlist)
- // instead of EnlistDistributedTransaction (which can),
- // however this should have been handled by the outer
- // connection which checks to ensure that it's OK. The
- // only case where we have the race condition is multiple
- // concurrent enlist requests to the same connection, which
- // is a bit out of line with something we should have to
- // support.
- // enlisted transaction can be nullified in Dispose call without lock
- previousTransactionClone = Interlocked.Exchange(ref _enlistedTransaction, valueClone);
- _enlistedTransactionOriginal = value;
- value = valueClone;
- valueClone = null; // we've stored it, don't dispose it.
- }
- }
- finally {
- // we really need to dispose our clones; they may have
- // native resources and GC may not happen soon enough.
- // VSDevDiv 479564: don't dispose if still holding reference in _enlistedTransaction
- if (null != previousTransactionClone &&
- !Object.ReferenceEquals(previousTransactionClone, _enlistedTransaction)) {
- previousTransactionClone.Dispose();
- }
- if (null != valueClone && !Object.ReferenceEquals(valueClone, _enlistedTransaction)) {
- valueClone.Dispose();
- }
- }
- // I don't believe that we need to lock to protect the actual
- // enlistment in the transaction; it would only protect us
- // against multiple concurrent calls to enlist, which really
- // isn't supported anyway.
- if (null != value) {
- if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
- int x = value.GetHashCode();
- Bid.PoolerTrace("<prov.DbConnectionInternal.set_EnlistedTransaction|RES|CPOOL> %d#, Transaction %d#, Enlisting.\n", ObjectID, x);
- }
- TransactionOutcomeEnlist(value);
- }
- }
- }
- }
- /// <summary>
- /// Get boolean value that indicates whether the enlisted transaction has been disposed.
- /// </summary>
- /// <value>
- /// True if there is an enlisted transaction, and it has been diposed.
- /// False if there is an enlisted transaction that has not been disposed, or if the transaction reference is null.
- /// </value>
- /// <remarks>
- /// This method must be called while holding a lock on the DbConnectionInternal instance.
- /// </remarks>
- protected bool EnlistedTransactionDisposed
- {
- get
- {
- // Until the Transaction.Disposed property is public it is necessary to access a member
- // that throws if the object is disposed to determine if in fact the transaction is disposed.
- try
- {
- bool disposed;
- SysTx.Transaction currentEnlistedTransactionOriginal = _enlistedTransactionOriginal;
- if (currentEnlistedTransactionOriginal != null)
- {
- disposed = currentEnlistedTransactionOriginal.TransactionInformation == null;
- }
- else
- {
- // Don't expect to get here in the general case,
- // Since this getter is called by CheckEnlistedTransactionBinding
- // after checking for a non-null enlisted transaction (and it does so under lock).
- disposed = false;
- }
- return disposed;
- }
- catch (ObjectDisposedException)
- {
- return true;
- }
- }
- }
- // Is this connection in stasis, waiting for transaction to end before returning to pool?
- internal bool IsTxRootWaitingForTxEnd {
- get {
- return _isInStasis;
- }
- }
- /// <summary>
- /// Get boolean that specifies whether an enlisted transaction can be unbound from
- /// the connection when that transaction completes.
- /// </summary>
- /// <value>
- /// True if the enlisted transaction can be unbound on transaction completion; otherwise false.
- /// </value>
- virtual protected bool UnbindOnTransactionCompletion
- {
- get
- {
- return true;
- }
- }
- // Is this a connection that must be put in stasis (or is already in stasis) pending the end of it's transaction?
- virtual protected internal bool IsNonPoolableTransactionRoot {
- get {
- return false; // if you want to have delegated transactions that are non-poolable, you better override this...
- }
- }
- virtual internal bool IsTransactionRoot {
- get {
- return false; // if you want to have delegated transactions, you better override this...
- }
- }
- protected internal bool IsConnectionDoomed {
- get {
- return _connectionIsDoomed;
- }
- }
- internal bool IsEmancipated {
- get {
- // NOTE: There are race conditions between PrePush, PostPop and this
- // property getter -- only use this while this object is locked;
- // (DbConnectionPool.Clear and ReclaimEmancipatedObjects
- // do this for us)
- // Remember how this works (I keep getting confused...)
- //
- // _pooledCount is incremented when the connection is pushed into the pool
- // _pooledCount is decremented when the connection is popped from the pool
- // _pooledCount is set to -1 when the connection is not pooled (just in case...)
- //
- // That means that:
- //
- // _pooledCount > 1 connection is in the pool multiple times (this is a serious bug...)
- // _pooledCount == 1 connection is in the pool
- // _pooledCount == 0 connection is out of the pool
- // _pooledCount == -1 connection is not a pooled connection; we shouldn't be here for non-pooled connections.
- // _pooledCount < -1 connection out of the pool multiple times (not sure how this could happen...)
- //
- // Now, our job is to return TRUE when the connection is out
- // of the pool and it's owning object is no longer around to
- // return it.
- bool value = !IsTxRootWaitingForTxEnd && (_pooledCount < 1) && !_owningObject.IsAlive;
- return value;
- }
- }
- internal bool IsInPool {
- get {
- Debug.Assert(_pooledCount <= 1 && _pooledCount >= -1, "Pooled count for object is invalid");
- return (_pooledCount == 1);
- }
- }
- internal int ObjectID {
- get {
- return _objectID;
- }
- }
- protected internal object Owner {
- // We use a weak reference to the owning object so we can identify when
- // it has been garbage collected without thowing exceptions.
- get {
- return _owningObject.Target;
- }
- }
- internal DbConnectionPool Pool {
- get {
- return _connectionPool;
- }
- }
- protected DbConnectionPoolCounters PerformanceCounters {
- get {
- return _performanceCounters;
- }
- }
- virtual protected bool ReadyToPrepareTransaction {
- get {
- return true;
- }
- }
- protected internal DbReferenceCollection ReferenceCollection {
- get {
- return _referenceCollection;
- }
- }
- abstract public string ServerVersion {
- get;
- }
- // this should be abstract but untill it is added to all the providers virtual will have to do [....]
- virtual public string ServerVersionNormalized {
- get{
- throw ADP.NotSupported();
- }
- }
- public bool ShouldHidePassword {
- get {
- return _hidePassword;
- }
- }
- public ConnectionState State {
- get {
- return _state;
- }
- }
- abstract protected void Activate(SysTx.Transaction transaction);
- internal void ActivateConnection(SysTx.Transaction transaction) {
- // Internal method called from the connection pooler so we don't expose
- // the Activate method publicly.
- Bid.PoolerTrace("<prov.DbConnectionInternal.ActivateConnection|RES|INFO|CPOOL> %d#, Activating\n", ObjectID);
- #if DEBUG
- int activateCount = Interlocked.Increment(ref _activateCount);
- Debug.Assert(1 == activateCount, "activated multiple times?");
- #endif // DEBUG
- Activate(transaction);
- #if !MOBILE
- PerformanceCounters.NumberOfActiveConnections.Increment();
- #endif
- }
- internal void AddWeakReference(object value, int tag) {
- if (null == _referenceCollection) {
- _referenceCollection = CreateReferenceCollection();
- if (null == _referenceCollection) {
- throw ADP.InternalError(ADP.InternalErrorCode.CreateReferenceCollectionReturnedNull);
- }
- }
- _referenceCollection.Add(value, tag);
- }
- abstract public DbTransaction BeginTransaction(IsolationLevel il);
- virtual public void ChangeDatabase(string value) {
- throw ADP.MethodNotImplemented("ChangeDatabase");
- }
- internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory) {
- // The implementation here is the implementation required for the
- // "open" internal connections, since our own private "closed"
- // singleton internal connection objects override this method to
- // prevent anything funny from happening (like disposing themselves
- // or putting them into a connection pool)
- //
- // Derived class should override DbConnectionInternal.Deactivate and DbConnectionInternal.Dispose
- // for cleaning up after DbConnection.Close
- // protected override void Deactivate() { // override DbConnectionInternal.Close
- // // do derived class connection deactivation for both pooled & non-pooled connections
- // }
- // public override void Dispose() { // override DbConnectionInternal.Close
- // // do derived class cleanup
- // base.Dispose();
- // }
- //
- // overriding DbConnection.Close is also possible, but must provider for their own synchronization
- // public override void Close() { // override DbConnection.Close
- // base.Close();
- // // do derived class outer connection for both pooled & non-pooled connections
- // // user must do their own synchronization here
- // }
- //
- // if the DbConnectionInternal derived class needs to close the connection it should
- // delegate to the DbConnection if one exists or directly call dispose
- // DbConnection owningObject = (DbConnection)Owner;
- // if (null != owningObject) {
- // owningObject.Close(); // force the closed state on the outer object.
- // }
- // else {
- // Dispose();
- // }
- //
- ////////////////////////////////////////////////////////////////
- // DON'T MESS WITH THIS CODE UNLESS YOU KNOW WHAT YOU'RE DOING!
- ////////////////////////////////////////////////////////////////
- Debug.Assert(null != owningObject, "null owningObject");
- Debug.Assert(null != connectionFactory, "null connectionFactory");
- Bid.PoolerTrace("<prov.DbConnectionInternal.CloseConnection|RES|CPOOL> %d# Closing.\n", ObjectID);
- // if an exception occurs after the state change but before the try block
- // the connection will be stuck in OpenBusy state. The commented out try-catch
- // block doesn't really help because a ThreadAbort during the finally block
- // would just refert the connection to a bad state.
- // Open->Closed: guarantee internal connection is returned to correct pool
- if (connectionFactory.SetInnerConnectionFrom(owningObject, DbConnectionOpenBusy.SingletonInstance, this)) {
-
- // Lock to prevent race condition with cancellation
- lock (this) {
- object lockToken = ObtainAdditionalLocksForClose();
- try {
- PrepareForCloseConnection();
- DbConnectionPool connectionPool = Pool;
- // Detach from enlisted transactions that are no longer active on close
- DetachCurrentTransactionIfEnded();
- // The singleton closed classes won't have owners and
- // connection pools, and we won't want to put them back
- // into the pool.
- if (null != connectionPool) {
- connectionPool.PutObject(this, owningObject); // PutObject calls Deactivate for us...
- // NOTE: Before we leave the PutObject call, another
- // thread may have already popped the connection from
- // the pool, so don't expect to be able to verify it.
- }
- else {
- Deactivate(); // ensure we de-activate non-pooled connections, or the data readers and transactions may not get cleaned up...
- #if !MOBILE
- PerformanceCounters.HardDisconnectsPerSecond.Increment();
- #endif
-
- // To prevent an endless recursion, we need to clear
- // the owning object before we call dispose so that
- // we can't get here a second time... Ordinarily, I
- // would call setting the owner to null a hack, but
- // this is safe since we're about to dispose the
- // object and it won't have an owner after that for
- // certain.
- _owningObject.Target = null;
- if (IsTransactionRoot) {
- SetInStasis();
- }
- else {
- #if MONO_PARTIAL_DATA_IMPORT
- Dispose();
- #else
- #if !MOBILE
- PerformanceCounters.NumberOfNonPooledConnections.Decrement();
- #endif
- if (this.GetType() != typeof(System.Data.SqlClient.SqlInternalConnectionSmi))
- {
- Dispose();
- }
- #endif
- }
- }
- }
- finally {
- ReleaseAdditionalLocksForClose(lockToken);
- // if a ThreadAbort puts us here then its possible the outer connection will not reference
- // this and this will be orphaned, not reclaimed by object pool until outer connection goes out of scope.
- connectionFactory.SetInnerConnectionEvent(owningObject, DbConnectionClosedPreviouslyOpened.SingletonInstance);
- }
- }
- }
- }
- virtual internal void PrepareForReplaceConnection() {
- // By default, there is no preperation required
- }
- virtual protected void PrepareForCloseConnection() {
- // By default, there is no preperation required
- }
- virtual protected object ObtainAdditionalLocksForClose() {
- return null; // no additional locks in default implementation
- }
- virtual protected void ReleaseAdditionalLocksForClose(object lockToken) {
- // no additional locks in default implementation
- }
- virtual protected DbReferenceCollection CreateReferenceCollection() {
- throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToConstructReferenceCollectionOnStaticObject);
- }
- abstract protected void Deactivate();
- internal void DeactivateConnection() {
- // Internal method called from the connection pooler so we don't expose
- // the Deactivate method publicly.
- Bid.PoolerTrace("<prov.DbConnectionInternal.DeactivateConnection|RES|INFO|CPOOL> %d#, Deactivating\n", ObjectID);
- #if DEBUG
- int activateCount = Interlocked.Decrement(ref _activateCount);
- Debug.Assert(0 == activateCount, "activated multiple times?");
- #endif // DEBUG
- #if !MOBILE
- if (PerformanceCounters != null) { // Pool.Clear will DestroyObject that will clean performanceCounters before going here
- PerformanceCounters.NumberOfActiveConnections.Decrement();
- }
- #endif
- if (!_connectionIsDoomed && Pool.UseLoadBalancing) {
- // If we're not already doomed, check the connection's lifetime and
- // doom it if it's lifetime has elapsed.
- DateTime now = DateTime.UtcNow; // WebData 111116
- if ((now.Ticks - _createTime.Ticks) > Pool.LoadBalanceTimeout.Ticks) {
- DoNotPoolThisConnection();
- }
- }
- Deactivate();
- }
- virtual internal void DelegatedTransactionEnded() {
- // Called by System.Transactions when the delegated transaction has
- // completed. We need to make closed connections that are in stasis
- // available again, or disposed closed/leaked non-pooled connections.
- // IMPORTANT NOTE: You must have taken a lock on the object before
- // you call this method to prevent race conditions with Clear and
- // ReclaimEmancipatedObjects.
- Bid.Trace("<prov.DbConnectionInternal.DelegatedTransactionEnded|RES|CPOOL> %d#, Delegated Transaction Completed.\n", ObjectID);
- if (1 == _pooledCount) {
- // When _pooledCount is 1, it indicates a closed, pooled,
- // connection so it is ready to put back into the pool for
- // general use.
- TerminateStasis(true);
- Deactivate(); // call it one more time just in case
- DbConnectionPool pool = Pool;
- if (null == pool) {
- throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectWithoutPool); // pooled connection does not have a pool
- }
- pool.PutObjectFromTransactedPool(this);
- }
- else if (-1 == _pooledCount && !_owningObject.IsAlive) {
- // When _pooledCount is -1 and the owning object no longer exists,
- // it indicates a closed (or leaked), non-pooled connection so
- // it is safe to dispose.
- TerminateStasis(false);
-
- Deactivate(); // call it one more time just in case
- // it's a non-pooled connection, we need to dispose of it
- // once and for all, or the server will have fits about us
- // leaving connections open until the client-side GC kicks
- // in.
- #if !MOBILE
- PerformanceCounters.NumberOfNonPooledConnections.Decrement();
- #endif
- Dispose();
- }
- // When _pooledCount is 0, the connection is a pooled connection
- // that is either open (if the owning object is alive) or leaked (if
- // the owning object is not alive) In either case, we can't muck
- // with the connection here.
- }
- public virtual void Dispose()
- {
- _connectionPool = null;
- _performanceCounters = null;
- _connectionIsDoomed = true;
- _enlistedTransactionOriginal = null; // should not be disposed
- // Dispose of the _enlistedTransaction since it is a clone
- // of the original reference.
- // VSDD 780271 - _enlistedTransaction can be changed by another thread (TX end event)
- SysTx.Transaction enlistedTransaction = Interlocked.Exchange(ref _enlistedTransaction, null);
- if (enlistedTransaction != null)
- {
- enlistedTransaction.Dispose();
- }
- }
- protected internal void DoNotPoolThisConnection() {
- _cannotBePooled = true;
- Bid.PoolerTrace("<prov.DbConnectionInternal.DoNotPoolThisConnection|RES|INFO|CPOOL> %d#, Marking pooled object as non-poolable so it will be disposed\n", ObjectID);
- }
- /// <devdoc>Ensure that this connection cannot be put back into the pool.</devdoc>
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- protected internal void DoomThisConnection() {
- _connectionIsDoomed = true;
- Bid.PoolerTrace("<prov.DbConnectionInternal.DoomThisConnection|RES|INFO|CPOOL> %d#, Dooming\n", ObjectID);
- }
- abstract public void EnlistTransaction(SysTx.Transaction transaction);
- virtual protected internal DataTable GetSchema(DbConnectionFactory factory, DbConnectionPoolGroup poolGroup, DbConnection outerConnection, string collectionName, string[] restrictions){
- Debug.Assert(outerConnection != null,"outerConnection may not be null.");
- DbMetaDataFactory metaDataFactory = factory.GetMetaDataFactory(poolGroup, this);
- Debug.Assert(metaDataFactory != null,"metaDataFactory may not be null.");
- return metaDataFactory.GetSchema(outerConnection, collectionName,restrictions);
- }
- internal void MakeNonPooledObject(object owningObject, DbConnectionPoolCounters performanceCounters) {
- // Used by DbConnectionFactory to indicate that this object IS NOT part of
- // a connection pool.
- _connectionPool = null;
- _performanceCounters = performanceCounters;
- _owningObject.Target = owningObject;
- _pooledCount = -1;
- }
- internal void MakePooledConnection(DbConnectionPool connectionPool) {
- // Used by DbConnectionFactory to indicate that this object IS part of
- // a connection pool.
- //
- _createTime = DateTime.UtcNow; // WebData 111116
- _connectionPool = connectionPool;
- _performanceCounters = connectionPool.PerformanceCounters;
- }
- internal void NotifyWeakReference(int message) {
- DbReferenceCollection referenceCollection = ReferenceCollection;
- if (null != referenceCollection) {
- referenceCollection.Notify(message);
- }
- }
- internal virtual void OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) {
- if (!TryOpenConnection(outerConnection, connectionFactory, null, null)) {
- throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending);
- }
- }
- /// <devdoc>The default implementation is for the open connection objects, and
- /// it simply throws. Our private closed-state connection objects
- /// override this and do the correct thing.</devdoc>
- // User code should either override DbConnectionInternal.Activate when it comes out of the pool
- // or override DbConnectionFactory.CreateConnection when the connection is created for non-pooled connections
- internal virtual bool TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
- throw ADP.ConnectionAlreadyOpen(State);
- }
- internal virtual bool TryReplaceConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
- throw ADP.MethodNotImplemented("TryReplaceConnection");
- }
- protected bool TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource<DbConnectionInternal> retry, DbConnectionOptions userOptions) {
- // ?->Connecting: prevent set_ConnectionString during Open
- if (connectionFactory.SetInnerConnectionFrom(outerConnection, DbConnectionClosedConnecting.SingletonInstance, this)) {
- DbConnectionInternal openConnection = null;
- try {
- connectionFactory.PermissionDemand(outerConnection);
- if (!connectionFactory.TryGetConnection(outerConnection, retry, userOptions, this, out openConnection)) {
- return false;
- }
- }
- catch {
- // This should occure for all exceptions, even ADP.UnCatchableExceptions.
- connectionFactory.SetInnerConnectionTo(outerConnection, this);
- throw;
- }
- if (null == openConnection) {
- connectionFactory.SetInnerConnectionTo(outerConnection, this);
- throw ADP.InternalConnectionError(ADP.ConnectionError.GetConnectionReturnsNull);
- }
- connectionFactory.SetInnerConnectionEvent(outerConnection, openConnection);
- }
- return true;
- }
- internal void PrePush(object expectedOwner) {
- // Called by DbConnectionPool when we're about to be put into it's pool, we
- // take this opportunity to ensure ownership and pool counts are legit.
- // IMPORTANT NOTE: You must have taken a lock on the object before
- // you call this method to prevent race conditions with Clear and
- // ReclaimEmancipatedObjects.
- //3 // The following tests are retail assertions of things we can't allow to happen.
- if (null == expectedOwner) {
- if (null != _owningObject.Target) {
- throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasOwner); // new unpooled object has an owner
- }
- }
- else if (_owningObject.Target != expectedOwner) {
- throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // unpooled object has incorrect owner
- }
- if (0 != _pooledCount) {
- throw ADP.InternalError(ADP.InternalErrorCode.PushingObjectSecondTime); // pushing object onto stack a second time
- }
- if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
- //DbConnection x = (expectedOwner as DbConnection);
- Bid.PoolerTrace("<prov.DbConnectionInternal.PrePush|RES|CPOOL> %d#, Preparing to push into pool, owning connection %d#, pooledCount=%d\n", ObjectID, 0, _pooledCount);
- }
- _pooledCount++;
- _owningObject.Target = null; // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2%
- }
- internal void PostPop(object newOwner) {
- // Called by DbConnectionPool right after it pulls this from it's pool, we
- // take this opportunity to ensure ownership and pool counts are legit.
- Debug.Assert(!IsEmancipated,"pooled object not in pool");
-
- // SQLBUDT #356871 -- When another thread is clearing this pool, it
- // will doom all connections in this pool without prejudice which
- // causes the following assert to fire, which really mucks up stress
- // against checked bits. The assert is benign, so we're commenting
- // it out.
- //Debug.Assert(CanBePooled, "pooled object is not poolable");
- // IMPORTANT NOTE: You must have taken a lock on the object before
- // you call this method to prevent race conditions with Clear and
- // ReclaimEmancipatedObjects.
- if (null != _owningObject.Target) {
- throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectHasOwner); // pooled connection already has an owner!
- }
- _owningObject.Target = newOwner;
- _pooledCount--;
- if (Bid.IsOn(DbConnectionPool.PoolerTracePoints)) {
- //DbConnection x = (newOwner as DbConnection);
- Bid.PoolerTrace("<prov.DbConnectionInternal.PostPop|RES|CPOOL> %d#, Preparing to pop from pool, owning connection %d#, pooledCount=%d\n", ObjectID, 0, _pooledCount);
- }
- //3 // The following tests are retail assertions of things we can't allow to happen.
- if (null != Pool) {
- if (0 != _pooledCount) {
- throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectInPoolMoreThanOnce); // popping object off stack with multiple pooledCount
- }
- }
- else if (-1 != _pooledCount) {
- throw ADP.InternalError(ADP.InternalErrorCode.NonPooledObjectUsedMoreThanOnce); // popping object off stack with multiple pooledCount
- }
- }
- internal void RemoveWeakReference(object value) {
- DbReferenceCollection referenceCollection = ReferenceCollection;
- if (null != referenceCollection) {
- referenceCollection.Remove(value);
- }
- }
- // Cleanup connection's transaction-specific structures (currently used by Delegated transaction).
- // This is a separate method because cleanup can be triggered in multiple ways for a delegated
- // transaction.
- virtual protected void CleanupTransactionOnCompletion(SysTx.Transaction transaction) {
- }
- internal void DetachCurrentTransactionIfEnded() {
- SysTx.Transaction enlistedTransaction = EnlistedTransaction;
- if (enlistedTransaction != null) {
- bool transactionIsDead;
- try {
- transactionIsDead = (SysTx.TransactionStatus.Active != enlistedTransaction.TransactionInformation.Status);
- }
- catch (SysTx.TransactionException) {
- // If the transaction is being processed (i.e. is part way through a rollback\commit\etc then TransactionInformation.Status will throw an exception)
- transactionIsDead = true;
- }
- if (transactionIsDead) {
- DetachTransaction(enlistedTransaction, true);
- }
- }
- }
- // Detach transaction from connection.
- internal void DetachTransaction(SysTx.Transaction transaction, bool isExplicitlyReleasing) {
- Bid.Trace("<prov.DbConnectionInternal.DetachTransaction|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", ObjectID, _pooledCount);
- // potentially a multi-threaded event, so lock the connection to make sure we don't enlist in a new
- // transaction between compare and assignment. No need to short circuit outside of lock, since failed comparisons should
- // be the exception, not the rule.
- lock (this) {
- // Detach if detach-on-end behavior, or if outer connection was closed
- DbConnection owner = (DbConnection)Owner;
- if (isExplicitlyReleasing || UnbindOnTransactionCompletion || null == owner) {
- SysTx.Transaction currentEnlistedTransaction = _enlistedTransaction;
- if (currentEnlistedTransaction != null && transaction.Equals(currentEnlistedTransaction)) {
- EnlistedTransaction = null;
- if (IsTxRootWaitingForTxEnd) {
- DelegatedTransactionEnded();
- }
- }
- }
- }
- }
- // Handle transaction detach, pool cleanup and other post-transaction cleanup tasks associated with
- internal void CleanupConnectionOnTransactionCompletion(SysTx.Transaction transaction) {
- DetachTransaction(transaction, false);
- DbConnectionPool pool = Pool;
- if (null != pool) {
- pool.TransactionEnded(transaction, this);
- }
- }
- void TransactionCompletedEvent(object sender, SysTx.TransactionEventArgs e) {
- SysTx.Transaction transaction = e.Transaction;
- Bid.Trace("<prov.DbConnectionInternal.TransactionCompletedEvent|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", ObjectID, _pooledCount);
- CleanupTransactionOnCompletion(transaction);
- CleanupConnectionOnTransactionCompletion(transaction);
- }
- //
- [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)]
- private void TransactionOutcomeEnlist(SysTx.Transaction transaction) {
- transaction.TransactionCompleted += new SysTx.TransactionCompletedEventHandler(TransactionCompletedEvent);
- }
- internal void SetInStasis() {
- _isInStasis = true;
- Bid.PoolerTrace("<prov.DbConnectionInternal.SetInStasis|RES|CPOOL> %d#, Non-Pooled Connection has Delegated Transaction, waiting to Dispose.\n", ObjectID);
- #if !MOBILE
- PerformanceCounters.NumberOfStasisConnections.Increment();
- #endif
- }
- private void TerminateStasis(bool returningToPool) {
- if (returningToPool) {
- Bid.PoolerTrace("<prov.DbConnectionInternal.TerminateStasis|RES|CPOOL> %d#, Delegated Transaction has ended, connection is closed. Returning to general pool.\n", ObjectID);
- }
- else {
- Bid.PoolerTrace("<prov.DbConnectionInternal.TerminateStasis|RES|CPOOL> %d#, Delegated Transaction has ended, connection is closed/leaked. Disposing.\n", ObjectID);
- }
- #if !MOBILE
- PerformanceCounters.NumberOfStasisConnections.Decrement();
- #endif
- _isInStasis = false;
- }
- /// <summary>
- /// When overridden in a derived class, will check if the underlying connection is still actually alive
- /// </summary>
- /// <param name="throwOnException">If true an exception will be thrown if the connection is dead instead of returning true\false
- /// (this allows the caller to have the real reason that the connection is not alive (e.g. network error, etc))</param>
- /// <returns>True if the connection is still alive, otherwise false (If not overridden, then always true)</returns>
- internal virtual bool IsConnectionAlive(bool throwOnException = false)
- {
- return true;
- }
- }
- }
|