Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Data / System / Data / SqlClient / SqlInternalConnection.cs / 1305376 / SqlInternalConnection.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //----------------------------------------------------------------------------- namespace System.Data.SqlClient { using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Data.ProviderBase; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using SysTx = System.Transactions; abstract internal class SqlInternalConnection : DbConnectionInternal { private readonly SqlConnectionString _connectionOptions; private bool _isEnlistedInTransaction; // is the server-side connection enlisted? true while we're enlisted, reset only after we send a null... private byte[] _promotedDTCToken; // token returned by the server when we promote transaction private byte[] _whereAbouts; // cache the whereabouts (DTC Address) for exporting // if connection is not open: null // if connection is open: currently active database internal string CurrentDatabase { get; set; } // if connection is not open yet, CurrentDataSource is null // if connection is open: // * for regular connections, it is set to Data Source value from connection string // * for connections with FailoverPartner, it is set to the FailoverPartner value from connection string if the connection was opened to it. internal string CurrentDataSource { get; set; } // the delegated (or promoted) transaction we're responsible for. internal SqlDelegatedTransaction DelegatedTransaction { get; set; } internal enum TransactionRequest { Begin, Promote, Commit, Rollback, IfRollback, Save }; internal SqlInternalConnection(SqlConnectionString connectionOptions) : base() { Debug.Assert(null != connectionOptions, "null connectionOptions?"); _connectionOptions = connectionOptions; } internal SqlConnection Connection { get { return (SqlConnection)Owner; } } internal SqlConnectionString ConnectionOptions { get { return _connectionOptions; } } abstract internal SqlInternalTransaction CurrentTransaction { get; } // SQLBU 415870 // Get the internal transaction that should be hooked to a new outer transaction // during a BeginTransaction API call. In some cases (i.e. connection is going to // be reset), CurrentTransaction should not be hooked up this way. virtual internal SqlInternalTransaction AvailableInternalTransaction { get { return CurrentTransaction; } } abstract internal SqlInternalTransaction PendingTransaction { get; } override protected internal bool IsNonPoolableTransactionRoot { get { return IsTransactionRoot; // default behavior is that root transactions are NOT poolable. Subclasses may override. } } override internal bool IsTransactionRoot { get { if (null == DelegatedTransaction) { return false; } lock (this) { return (null != DelegatedTransaction && DelegatedTransaction.IsActive); } } } internal bool HasLocalTransaction { get { SqlInternalTransaction currentTransaction = CurrentTransaction; bool result = (null != currentTransaction && currentTransaction.IsLocal); return result; } } internal bool HasLocalTransactionFromAPI { get { SqlInternalTransaction currentTransaction = CurrentTransaction; bool result = (null != currentTransaction && currentTransaction.HasParentTransaction); return result; } } internal bool IsEnlistedInTransaction { get { return _isEnlistedInTransaction; } } abstract internal bool IsLockedForBulkCopy { get; } abstract internal bool IsShiloh { get; } abstract internal bool IsYukonOrNewer { get; } abstract internal bool IsKatmaiOrNewer { get; } internal byte[] PromotedDTCToken { get { return _promotedDTCToken; } set { _promotedDTCToken = value; } } abstract internal void AddPreparedCommand(SqlCommand cmd); override public DbTransaction BeginTransaction(IsolationLevel iso) { return BeginSqlTransaction(iso, null); } virtual internal SqlTransaction BeginSqlTransaction(IsolationLevel iso, string transactionName) { SqlStatistics statistics = null; SNIHandle 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); statistics = SqlStatistics.StartTimer(Connection.Statistics); SqlConnection.ExecutePermission.Demand(); // MDAC 81476 ValidateConnectionForExecute(null); if (HasLocalTransactionFromAPI) throw ADP.ParallelTransactionsNotSupported(Connection); if (iso == IsolationLevel.Unspecified) { iso = IsolationLevel.ReadCommitted; // Default to ReadCommitted if unspecified. } SqlTransaction transaction = new SqlTransaction(this, Connection, iso, AvailableInternalTransaction); ExecuteTransaction(TransactionRequest.Begin, transactionName, iso, transaction.InternalTransaction, false); return 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; } finally { SqlStatistics.StopTimer(statistics); } } override public void ChangeDatabase(string database) { SqlConnection.ExecutePermission.Demand(); // MDAC 80961 if (ADP.IsEmpty(database)) { throw ADP.EmptyDatabaseName(); } ValidateConnectionForExecute(null); // ChangeDatabaseInternal(database); // do the real work... } abstract protected void ChangeDatabaseInternal(string database); abstract internal void ClearPreparedCommands(); // SQLBU 411265 // prepared commands need to be cleared out before the outer connection disconnects from the inner connection // Overriding CloseConnection allows us to do this. Long term, it would be better to manage this entirely on // the internal connection, but that's a much larger change. internal override void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory) { if (!IsConnectionDoomed) { ClearPreparedCommands(); } base.CloseConnection(owningObject, connectionFactory); } override protected void CleanupTransactionOnCompletion(SysTx.Transaction transaction) { // Note: unlocked, potentially multi-threaded code, so pull delegate to local to // ensure it doesn't change between test and call. SqlDelegatedTransaction delegatedTransaction = DelegatedTransaction; if (null != delegatedTransaction) { delegatedTransaction.TransactionEnded(transaction); } } override protected DbReferenceCollection CreateReferenceCollection() { return new SqlReferenceCollection(); } override protected void Deactivate() { if (Bid.AdvancedOn) { Bid.Trace("%d# deactivating\n", base.ObjectID); } SNIHandle 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); SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection; if (null != referenceCollection) { referenceCollection.Deactivate(); } // Invoke subclass-specific deactivation logic InternalDeactivate(); } #if DEBUG finally { tdsReliabilitySection.Stop(); } #endif //DEBUG } catch (System.OutOfMemoryException) { DoomThisConnection(); throw; } catch (System.StackOverflowException) { DoomThisConnection(); throw; } catch (System.Threading.ThreadAbortException) { DoomThisConnection(); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } catch (Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; } // if an exception occurred, the inner connection will be // marked as unusable and destroyed upon returning to the // pool DoomThisConnection(); ADP.TraceExceptionWithoutRethrow(e); } } abstract internal void DisconnectTransaction(SqlInternalTransaction internalTransaction); override public void Dispose() { _whereAbouts = null; base.Dispose(); } protected void Enlist(SysTx.Transaction tx) { // This method should not be called while the connection has a // reference to an active delegated transaction. // Manual enlistment via SqlConnection.EnlistTransaction // should catch this case and throw an exception. // // Automatic enlistment isn't possible because // Sys.Tx keeps the connection alive until the transaction is completed. Debug.Assert (!IsNonPoolableTransactionRoot, "cannot defect an active delegated transaction!"); // potential race condition, but it's an assert if (null == tx) { if (IsEnlistedInTransaction) { EnlistNull(); } else { // When IsEnlistedInTransaction is false, it means we are in one of two states: // 1. EnlistTransaction is null, so the connection is truly not enlisted in a transaction, or // 2. Connection is enlisted in a SqlDelegatedTransaction. // // For #2, we have to consider whether or not the delegated transaction is active. // If it is not active, we allow the enlistment in the NULL transaction. // // If it is active, technically this is an error. // However, no exception is thrown as this was the precedent (and this case is silently ignored, no error, but no enlistment either). // There are two mitigations for this: // 1. SqlConnection.EnlistTransaction checks that the enlisted transaction has completed before allowing a different enlistment. // 2. For debug builds, the assert at the beginning of this method checks for an enlistment in an active delegated transaction. SysTx.Transaction enlistedTransaction = EnlistedTransaction; if (enlistedTransaction != null && enlistedTransaction.TransactionInformation.Status != SysTx.TransactionStatus.Active) { EnlistNull(); } } } // Only enlist if it's different... else if (!tx.Equals(EnlistedTransaction)) { // WebData 20000024 - Must use Equals, not != EnlistNonNull(tx); } } private void EnlistNonNull(SysTx.Transaction tx) { Debug.Assert(null != tx, "null transaction?"); if (Bid.AdvancedOn) { Bid.Trace(" %d#, transaction %d#.\n", base.ObjectID, tx.GetHashCode()); } bool hasDelegatedTransaction = false; if (IsYukonOrNewer) { if (Bid.AdvancedOn) { Bid.Trace(" %d#, attempting to delegate\n", base.ObjectID); } // Promotable transactions are only supported on Yukon // servers or newer. SqlDelegatedTransaction delegatedTransaction = new SqlDelegatedTransaction(this, tx); try { // NOTE: System.Transactions claims to resolve all // potential race conditions between multiple delegate // requests of the same transaction to different // connections in their code, such that only one // attempt to delegate will succeed. // NOTE: PromotableSinglePhaseEnlist will eventually // make a round trip to the server; doing this inside // a lock is not the best choice. We presume that you // aren't trying to enlist concurrently on two threads // and leave it at that -- We don't claim any thread // safety with regard to multiple concurrent requests // to enlist the same connection in different // transactions, which is good, because we don't have // it anyway. // PromotableSinglePhaseEnlist may not actually promote // the transaction when it is already delegated (this is // the way they resolve the race condition when two // threads attempt to delegate the same Lightweight // Transaction) In that case, we can safely ignore // our delegated transaction, and proceed to enlist // in the promoted one. if (tx.EnlistPromotableSinglePhase(delegatedTransaction)) { hasDelegatedTransaction = true; this.DelegatedTransaction = delegatedTransaction; if (Bid.AdvancedOn) { long transactionId = SqlInternalTransaction.NullTransactionId; int transactionObjectID = 0; if (null != CurrentTransaction) { transactionId = CurrentTransaction.TransactionId; transactionObjectID = CurrentTransaction.ObjectID; } Bid.Trace(" %d#, delegated to transaction %d# with transactionId=0x%I64x\n", base.ObjectID, transactionObjectID, transactionId); } } } catch (SqlException e) { // we do not want to eat the error if it is a fatal one if (e.Class >= TdsEnums.FATAL_ERROR_CLASS) { throw; } // if the parser is null or its state is not openloggedin, the connection is no longer good. SqlInternalConnectionTds tdsConnection = this as SqlInternalConnectionTds; if (tdsConnection != null) { TdsParser parser = tdsConnection.Parser; if (parser == null || parser.State != TdsParserState.OpenLoggedIn) { throw; } } ADP.TraceExceptionWithoutRethrow(e); // In this case, SqlDelegatedTransaction.Initialize // failed and we don't necessarily want to reject // things -- there may have been a legitimate reason // for the failure. } } if (!hasDelegatedTransaction) { if (Bid.AdvancedOn) { Bid.Trace(" %d#, delegation not possible, enlisting.\n", base.ObjectID); } byte[] cookie = null; if (null == _whereAbouts) { byte[] dtcAddress = GetDTCAddress(); if (null == dtcAddress) { throw SQL.CannotGetDTCAddress(); } _whereAbouts = dtcAddress; } cookie = GetTransactionCookie(tx, _whereAbouts); // send cookie to server to finish enlistment PropagateTransactionCookie(cookie); _isEnlistedInTransaction = true; if (Bid.AdvancedOn) { long transactionId = SqlInternalTransaction.NullTransactionId; int transactionObjectID = 0; if (null != CurrentTransaction) { transactionId = CurrentTransaction.TransactionId; transactionObjectID = CurrentTransaction.ObjectID; } Bid.Trace(" %d#, enlisted with transaction %d# with transactionId=0x%I64x\n", base.ObjectID, transactionObjectID, transactionId); } } EnlistedTransaction = tx; // Tell the base class about our enlistment // If we're on a Yukon or newer server, and we we delegate the // transaction successfully, we will have done a begin transaction, // which produces a transaction id that we should execute all requests // on. The TdsParser or SmiEventSink will store this information as // the current transaction. // // Likewise, propagating a transaction to a Yukon or newer server will // produce a transaction id that The TdsParser or SmiEventSink will // store as the current transaction. // // In either case, when we're working with a Yukon or newer server // we better have a current transaction by now. Debug.Assert(!IsYukonOrNewer || null != CurrentTransaction, "delegated/enlisted transaction with null current transaction?"); } internal void EnlistNull() { if (Bid.AdvancedOn) { Bid.Trace(" %d#, unenlisting.\n", base.ObjectID); } // We were in a transaction, but now we are not - so send // message to server with empty transaction - confirmed proper // behavior from Sameet Agarwal // // The connection pooler maintains separate pools for enlisted // transactions, and only when that transaction is committed or // rolled back will those connections be taken from that // separate pool and returned to the general pool of connections // that are not affiliated with any transactions. When this // occurs, we will have a new transaction of null and we are // required to send an empty transaction payload to the server. PropagateTransactionCookie(null); _isEnlistedInTransaction = false; EnlistedTransaction = null; // Tell the base class about our enlistment if (Bid.AdvancedOn) { Bid.Trace(" %d#, unenlisted.\n", base.ObjectID); } // The EnlistTransaction above will return an TransactionEnded event, // which causes the TdsParser or SmiEventSink should to clear the // current transaction. // // In either case, when we're working with a Yukon or newer server // we better not have a current transaction at this point. Debug.Assert(!IsYukonOrNewer || null == CurrentTransaction, "unenlisted transaction with non-null current transaction?"); // verify it! } 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 & Yukon, 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. SNIHandle 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; } } abstract internal void ExecuteTransaction(TransactionRequest transactionRequest, string name, IsolationLevel iso, SqlInternalTransaction internalTransaction, bool isDelegateControlRequest); internal SqlDataReader FindLiveReader(SqlCommand command) { SqlDataReader reader = null; SqlReferenceCollection referenceCollection = (SqlReferenceCollection)ReferenceCollection; if (null != referenceCollection) { reader = referenceCollection.FindLiveReader(command); } return reader; } static internal SNIHandle GetBestEffortCleanupTarget(SqlConnection connection) { if (null != connection) { SqlInternalConnectionTds innerConnection = (connection.InnerConnection as SqlInternalConnectionTds); if (null != innerConnection) { TdsParser parser = innerConnection.Parser; if (parser != null) { return parser.GetBestEffortCleanupTarget(); } } } return null; } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] static internal void BestEffortCleanup(SNIHandle target) { if (null != target) { target.Dispose(); } } abstract protected byte[] GetDTCAddress(); static private byte[] GetTransactionCookie(SysTx.Transaction transaction, byte[] whereAbouts) { byte[] transactionCookie = null; if (null != transaction) { transactionCookie = SysTx.TransactionInterop.GetExportCookie(transaction, whereAbouts); } return transactionCookie; } virtual protected void InternalDeactivate() { } internal void OnError(SqlException exception, bool breakConnection) { if (breakConnection) DoomThisConnection(); if (null != Connection) { Connection.OnError(exception, breakConnection); } else if (exception.Class >= TdsEnums.MIN_ERROR_CLASS) { // It is an error, and should be thrown. Class of TdsEnums.MIN_ERROR_CLASS // or above is an error, below TdsEnums.MIN_ERROR_CLASS denotes an info message. throw exception; } } abstract protected void PropagateTransactionCookie(byte[] transactionCookie); abstract internal void RemovePreparedCommand(SqlCommand cmd); abstract internal void ValidateConnectionForExecute(SqlCommand command); } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Region.cs
- MultiByteCodec.cs
- ArgumentElement.cs
- ObjectResult.cs
- PowerStatus.cs
- OperatorExpressions.cs
- AnimationLayer.cs
- UnmanagedMarshal.cs
- DetailsViewDeletedEventArgs.cs
- MetadataException.cs
- StorageEntityContainerMapping.cs
- SqlDataSourceSelectingEventArgs.cs
- FloatMinMaxAggregationOperator.cs
- EventMappingSettings.cs
- SystemGatewayIPAddressInformation.cs
- VBCodeProvider.cs
- Funcletizer.cs
- View.cs
- ExceptionNotification.cs
- BaseValidator.cs
- TrackBar.cs
- TypeUtil.cs
- TextContainerChangeEventArgs.cs
- DelegatingHeader.cs
- WindowsListViewItem.cs
- ByeMessageApril2005.cs
- GlyphRun.cs
- KeyValuePair.cs
- PhonemeEventArgs.cs
- DesignerHelpers.cs
- PropertyValueUIItem.cs
- ConfigurationFileMap.cs
- TextLineBreak.cs
- DataGridRowEventArgs.cs
- MultilineStringConverter.cs
- CornerRadius.cs
- StateItem.cs
- Int32RectConverter.cs
- NetworkInterface.cs
- MultipleViewProviderWrapper.cs
- SqlXml.cs
- CompositeActivityValidator.cs
- ProcessHostMapPath.cs
- OperationAbortedException.cs
- DataStreamFromComStream.cs
- LocalServiceSecuritySettings.cs
- QueryOperationResponseOfT.cs
- DbCommandTree.cs
- XmlUtf8RawTextWriter.cs
- RuntimeCompatibilityAttribute.cs
- OleDbParameterCollection.cs
- BinaryFormatter.cs
- CodeComment.cs
- COSERVERINFO.cs
- Int64.cs
- CodeMemberEvent.cs
- MdiWindowListStrip.cs
- XmlHierarchicalDataSourceView.cs
- AuthenticationModuleElement.cs
- HttpCachePolicyWrapper.cs
- InputBuffer.cs
- ListViewDeleteEventArgs.cs
- ToolStripContentPanelRenderEventArgs.cs
- DataObject.cs
- EncryptedType.cs
- Stack.cs
- InvariantComparer.cs
- Int32Converter.cs
- MatrixTransform.cs
- _Connection.cs
- OutputCacheProfile.cs
- SafeCoTaskMem.cs
- ListenerElementsCollection.cs
- SolidColorBrush.cs
- DesignerDataSchemaClass.cs
- CodeCatchClauseCollection.cs
- DateTimeFormatInfo.cs
- RegexEditorDialog.cs
- URLEditor.cs
- DefinitionBase.cs
- TransferRequestHandler.cs
- XmlWriterTraceListener.cs
- FactoryMaker.cs
- CompilerTypeWithParams.cs
- ControlDesignerState.cs
- SafeThemeHandle.cs
- FragmentQuery.cs
- CustomActivityDesigner.cs
- DataGridViewRowHeaderCell.cs
- CharUnicodeInfo.cs
- NativeMethods.cs
- listitem.cs
- RouteItem.cs
- IconBitmapDecoder.cs
- AutomationElementCollection.cs
- ToolboxItem.cs
- CategoryValueConverter.cs
- Attributes.cs
- SqlEnums.cs
- ViewStateModeByIdAttribute.cs