2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="DbConnectionInternal.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
2017-08-21 15:34:15 +00:00
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
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
2017-08-21 15:34:15 +00:00
private int _activateCount ; // debug only counter to verify activate/deactivates are in sync.
2016-08-03 10:59:49 +00:00
#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 ;
}
2017-08-21 15:34:15 +00:00
// this should be abstract but untill it is added to all the providers virtual will have to do Microsoft
2016-08-03 10:59:49 +00:00
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 ;
}
}
}