public override void EnlistTransaction(System.Transactions.Transaction transaction) { ExecutePermission.Demand(); Bid.Trace("<prov.DbConnectionHelper.EnlistTransaction|RES|TRAN> %d#, Connection enlisting in a transaction.\n", this.ObjectID); System.Data.ProviderBase.DbConnectionInternal innerConnection = this.InnerConnection; System.Transactions.Transaction enlistedTransaction = innerConnection.EnlistedTransaction; if (enlistedTransaction != null) { if (enlistedTransaction.Equals(transaction)) { return; } if (enlistedTransaction.TransactionInformation.Status == System.Transactions.TransactionStatus.Active) { throw System.Data.Common.ADP.TransactionPresent(); } } innerConnection.EnlistTransaction(transaction); GC.KeepAlive(this); }
// Event notification that transaction ended. This comes from the subscription to the Transaction's // ended event via the internal connection. If it occurs without a prior Rollback or SinglePhaseCommit call, // it indicates the transaction was ended externally (generally that one the the DTC participants aborted // the transaction). internal void TransactionEnded(SysTx.Transaction transaction) { SqlInternalConnection connection = _connection; if (connection != null) { SqlClientEventSource.Log.TraceEvent("<sc.SqlDelegatedTransaction.TransactionEnded|RES|CPOOL> {0}, Connection {1}, transaction completed externally.", ObjectID, connection.ObjectID); lock (connection) { if (_atomicTransaction.Equals(transaction)) { // No need to validate active on connection, this operation can be called on completed transactions _active = false; _connection = null; } // Safest approach is to doom this connection, whose transaction has been aborted externally. // If we want to avoid dooming the connection for performance, state needs to be properly restored. (future TODO) connection.DoomThisConnection(); } } }
override public void EnlistTransaction(SysTx.Transaction transaction) { CONNECTIONOBJECTNAME.ExecutePermission.Demand(); Bid.Trace("<prov.DbConnectionHelper.EnlistTransaction|RES|TRAN> %d#, Connection enlisting in a transaction.\n", ObjectID); // If we're currently enlisted in a transaction and we were called // on the EnlistTransaction method (Whidbey) we're not allowed to // enlist in a different transaction. DbConnectionInternal innerConnection = InnerConnection; // NOTE: since transaction enlistment involves round trips to the // server, we don't want to lock here, we'll handle the race conditions // elsewhere. SysTx.Transaction enlistedTransaction = innerConnection.EnlistedTransaction; if (enlistedTransaction != null) { // Allow calling enlist if already enlisted (no-op) if (enlistedTransaction.Equals(transaction)) { return; } // Allow enlisting in a different transaction if the enlisted transaction has completed. if (enlistedTransaction.TransactionInformation.Status == SysTx.TransactionStatus.Active) { throw ADP.TransactionPresent(); } } RepairInnerConnection(); InnerConnection.EnlistTransaction(transaction); // NOTE: If this outer connection were to be GC'd while we're // enlisting, the pooler would attempt to reclaim the inner connection // while we're attempting to enlist; not sure how likely that is but // we should consider a GC.KeepAlive(this) here. GC.KeepAlive(this); }
// Detach transaction from connection. internal void DetachTransaction(SysTx.Transaction transaction, bool isExplicitlyReleasing) { // 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(); } } } } }
override public void EnlistTransaction(SysTx.Transaction transaction) { SqlConnection.VerifyExecutePermission(); ValidateConnectionForExecute(null); // If a connection has a local transaction outstanding and you try // to enlist in a DTC transaction, SQL Server will rollback the // local transaction and then do the enlist (7.0 and 2000). So, if // the user tries to do this, throw. if (HasLocalTransaction) { throw ADP.LocalTransactionPresent(); } if (null != transaction && transaction.Equals(EnlistedTransaction)) { // No-op if this is the current transaction return; } // If a connection is already enlisted in a DTC transaction and you // try to enlist in another one, in 7.0 the existing DTC transaction // would roll back and then the connection would enlist in the new // one. In SQL 2000 & 2005, when you enlist in a DTC transaction // while the connection is already enlisted in a DTC transaction, // the connection simply switches enlistments. Regardless, simply // enlist in the user specified distributed transaction. This // behavior matches OLEDB and ODBC. TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(Connection); Enlist(transaction); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException e) { Connection.Abort(e); throw; } catch (System.StackOverflowException e) { Connection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { Connection.Abort(e); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } }
internal void DetachTransaction(Transaction transaction, bool isExplicitlyReleasing) { Bid.Trace("<prov.DbConnectionInternal.DetachTransaction|RES|CPOOL> %d#, Transaction Completed. (pooledCount=%d)\n", this.ObjectID, this._pooledCount); lock (this) { DbConnection owner = (DbConnection) this.Owner; if ((isExplicitlyReleasing || this.UnbindOnTransactionCompletion) || (owner == null)) { Transaction transaction2 = this._enlistedTransaction; if ((transaction2 != null) && transaction.Equals(transaction2)) { this.EnlistedTransaction = null; if (this.IsTxRootWaitingForTxEnd) { this.DelegatedTransactionEnded(); } } } } }
private void PushServiceDomain( Transaction newCurrent ) { // If we are not changing the transaction for the current ServiceDomain then // don't call CoEnterServiceDomain if ((newCurrent != null && newCurrent.Equals( SysES.ContextUtil.SystemTransaction )) || (newCurrent == null && SysES.ContextUtil.SystemTransaction == null )) { return; } SysES.ServiceConfig serviceConfig = new SysES.ServiceConfig(); try { // If a transaction is specified place it in BYOT. Otherwise the // default transaction option for ServiceConfig is disabled. So // if this is a not supported scope it will be cleared. if ( newCurrent != null ) { // To work around an SWC bug in Com+ we need to create 2 // service domains. // Com+ will by default try to inherit synchronization from // an existing context. Turn that off. serviceConfig.Synchronization = SysES.SynchronizationOption.RequiresNew; SysES.ServiceDomain.Enter( serviceConfig ); this.createdDoubleServiceDomain = true; serviceConfig.Synchronization = SysES.SynchronizationOption.Required; serviceConfig.BringYourOwnSystemTransaction = newCurrent; } SysES.ServiceDomain.Enter( serviceConfig ); this.createdServiceDomain = true; } catch ( COMException e ) { if ( System.Transactions.Oletx.NativeMethods.XACT_E_NOTRANSACTION == e.ErrorCode ) { throw TransactionException.Create(SR.GetString(SR.TraceSourceBase), SR.GetString(SR.TransactionAlreadyOver), e, newCurrent == null ? Guid.Empty : newCurrent.DistributedTxId); } throw TransactionException.Create(SR.GetString(SR.TraceSourceBase), e.Message, e, newCurrent == null ? Guid.Empty : newCurrent.DistributedTxId); } finally { if ( !this.createdServiceDomain ) { // If we weren't successful in creating both service domains then // make sure to exit one of them. if ( this.createdDoubleServiceDomain ) { SysES.ServiceDomain.Leave(); } } } }
internal void RemoveReference(Transaction tx) { lock (this.mutex) { if (tx.Equals(this.current)) { if (this.waiting != null) { bool flag; this.current = this.waiting; this.waiting = null; if (this.instanceContext.Behavior.ReleaseServiceInstanceOnTransactionComplete) { this.instanceContext.ReleaseServiceInstance(); if (DiagnosticUtility.ShouldTraceInformation) { TraceUtility.TraceEvent(TraceEventType.Information, 0xe000c, System.ServiceModel.SR.GetString("TraceCodeTxReleaseServiceInstanceOnCompletion", new object[] { tx.TransactionInformation.LocalIdentifier })); } } this.paused.Resume(out flag); if (!flag) { } } else { this.shouldReleaseInstance = true; this.current = null; } } if ((this.pending != null) && this.pending.ContainsKey(tx)) { this.pending.Remove(tx); } } }
internal void RemoveReference(Transaction tx) { lock (this.mutex) { if (tx.Equals(this.current)) { if (this.waiting != null) { this.current = waiting; this.waiting = null; if (instanceContext.Behavior.ReleaseServiceInstanceOnTransactionComplete) { instanceContext.ReleaseServiceInstance(); if (DiagnosticUtility.ShouldTraceInformation) { TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.TxReleaseServiceInstanceOnCompletion, SR.GetString(SR.TraceCodeTxReleaseServiceInstanceOnCompletion, tx.TransactionInformation.LocalIdentifier) ); } } bool alreadyResumedNoLock; this.paused.Resume(out alreadyResumedNoLock); if (alreadyResumedNoLock) { Fx.Assert("TransactionBehavior resumed more than once for same call."); } } else { this.shouldReleaseInstance = true; this.current = null; } } if (this.pending != null) { if (this.pending.ContainsKey(tx)) { this.pending.Remove(tx); } } } }
public override void EnlistTransaction(Transaction transaction) { this.ValidateConnectionForExecute(null); if (this.HasLocalTransaction) { throw ADP.LocalTransactionPresent(); } if ((null == transaction) || !transaction.Equals(base.EnlistedTransaction)) { SNIHandle target = null; RuntimeHelpers.PrepareConstrainedRegions(); try { target = GetBestEffortCleanupTarget(this.Connection); this.Enlist(transaction); } catch (OutOfMemoryException exception3) { this.Connection.Abort(exception3); throw; } catch (StackOverflowException exception2) { this.Connection.Abort(exception2); throw; } catch (ThreadAbortException exception) { this.Connection.Abort(exception); BestEffortCleanup(target); throw; } } }
protected void Enlist(Transaction tx) { if (null == tx) { if (this.IsEnlistedInTransaction) { this.EnlistNull(); } else { Transaction enlistedTransaction = base.EnlistedTransaction; if ((enlistedTransaction != null) && (enlistedTransaction.TransactionInformation.Status != TransactionStatus.Active)) { this.EnlistNull(); } } } else if (!tx.Equals(base.EnlistedTransaction)) { this.EnlistNonNull(tx); } }
private void PushServiceDomain(Transaction newCurrent) { if (((newCurrent == null) || !newCurrent.Equals(ContextUtil.SystemTransaction)) && ((newCurrent != null) || (ContextUtil.SystemTransaction != null))) { ServiceConfig cfg = new ServiceConfig(); try { if (newCurrent != null) { cfg.Synchronization = SynchronizationOption.RequiresNew; ServiceDomain.Enter(cfg); this.createdDoubleServiceDomain = true; cfg.Synchronization = SynchronizationOption.Required; cfg.BringYourOwnSystemTransaction = newCurrent; } ServiceDomain.Enter(cfg); this.createdServiceDomain = true; } catch (COMException exception) { if (System.Transactions.Oletx.NativeMethods.XACT_E_NOTRANSACTION == exception.ErrorCode) { throw TransactionException.Create(System.Transactions.SR.GetString("TraceSourceBase"), System.Transactions.SR.GetString("TransactionAlreadyOver"), exception); } throw TransactionException.Create(System.Transactions.SR.GetString("TraceSourceBase"), exception.Message, exception); } finally { if (!this.createdServiceDomain && this.createdDoubleServiceDomain) { ServiceDomain.Leave(); } } } }