Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Data / System / Data / SqlClient / sqlinternaltransaction.cs / 4 / sqlinternaltransaction.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //----------------------------------------------------------------------------- namespace System.Data.SqlClient { using System.Data; using System.Data.Common; using System.Data.ProviderBase; using System.Data.Sql; using System.Data.SqlTypes; using System.Diagnostics; using System.Threading; internal enum TransactionState { Pending = 0, Active = 1, Aborted = 2, Committed = 3, Unknown = 4, } internal enum TransactionType { LocalFromTSQL = 1, LocalFromAPI = 2, Delegated = 3, Distributed = 4, Context = 5, // only valid in proc. }; sealed internal class SqlInternalTransaction { internal const long NullTransactionId = 0; private TransactionState _transactionState; private TransactionType _transactionType; private long _transactionId; // passed in the MARS headers private int _openResultCount; // passed in the MARS headers private SqlInternalConnection _innerConnection; private bool _disposing; // used to prevent us from throwing exceptions while we're disposing private WeakReference _parent; // weak ref to the outer transaction object; needs to be weak to allow GC to occur. private static int _objectTypeCount; // Bid counter internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction) : this(innerConnection, type, outerTransaction, NullTransactionId) { } internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) { Bid.PoolerTrace("%d#, Created for connection %d#, outer transaction %d#, Type %d\n", ObjectID, innerConnection.ObjectID, (null != outerTransaction) ? outerTransaction.ObjectID : -1, (int)type); _innerConnection = innerConnection; _transactionType = type; if (null != outerTransaction) { _parent = new WeakReference(outerTransaction); } _transactionId = transactionId; } internal bool HasParentTransaction { get { // Return true if we are an API started local transaction, or if we were a TSQL // started local transaction and were then wrapped with a parent transaction as // a result of a later API begin transaction. bool result = ( (TransactionType.LocalFromAPI == _transactionType) || (TransactionType.LocalFromTSQL == _transactionType && _parent != null) ); return result; } } internal bool IsAborted { get { return (TransactionState.Aborted == _transactionState); } } internal bool IsActive { get { return (TransactionState.Active == _transactionState); } } internal bool IsCommitted { get { return (TransactionState.Committed == _transactionState); } } internal bool IsCompleted { get { return (TransactionState.Aborted == _transactionState || TransactionState.Committed == _transactionState || TransactionState.Unknown == _transactionState); } } internal bool IsContext { get { bool result = (TransactionType.Context == _transactionType); return result; } } internal bool IsDelegated { get { bool result = (TransactionType.Delegated == _transactionType); return result; } } internal bool IsDistributed { get { bool result = (TransactionType.Distributed == _transactionType); return result; } } internal bool IsLocal { get { bool result = (TransactionType.LocalFromTSQL == _transactionType || TransactionType.LocalFromAPI == _transactionType || TransactionType.Context == _transactionType); return result; } } internal bool IsOrphaned { get { // An internal transaction is orphaned when its parent has been // reclaimed by GC. bool result; if (null == _parent) { // No parent, so we better be LocalFromTSQL. Should we even return in this case - // since it could be argued this is invalid? Debug.Assert(false, "Why are we calling IsOrphaned with no parent?"); Debug.Assert(_transactionType == TransactionType.LocalFromTSQL, "invalid state"); result = false; } else if (null == _parent.Target) { // We have an parent, but parent was GC'ed. result = true; } else { // We have an parent, and parent is alive. result = false; } return result; } } internal bool IsZombied { get { return (null == _innerConnection); } } internal int ObjectID { get { return _objectID; } } internal int OpenResultsCount { get { return _openResultCount; } } internal SqlTransaction Parent { get { SqlTransaction result = null; // Should we protect against this, since this probably is an invalid state? Debug.Assert(null != _parent, "Why are we calling Parent with no parent?"); if (null != _parent) { result = (SqlTransaction)_parent.Target; } return result; } } internal long TransactionId { get { return _transactionId; } set { Debug.Assert(NullTransactionId == _transactionId, "setting transaction cookie while one is active?"); _transactionId = value; } } internal void Activate () { _transactionState = TransactionState.Active; } private void CheckTransactionLevelAndZombie() { try { if (!IsZombied && GetServerTransactionLevel() == 0) { // If not zombied, not closed, and not in transaction, zombie. Zombie(); } } catch (Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; } ADP.TraceExceptionWithoutRethrow(e); Zombie(); // If exception caught when trying to check level, zombie. } } internal void CloseFromConnection() { SqlInternalConnection innerConnection = _innerConnection; Debug.Assert (innerConnection != null,"How can we be here if the connection is null?"); Bid.PoolerTrace(" %d#, Closing\n", ObjectID); bool processFinallyBlock = true; try { innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); } catch (Exception e) { processFinallyBlock = ADP.IsCatchableExceptionType(e); throw; } finally { Debug.Assert(null != Thread.GetData(TdsParser.ReliabilitySlot), "unreliable call to CloseFromConnection"); // you need to setup for a thread abort somewhere before you call this method if (processFinallyBlock) { // Always ensure we're zombied; Yukon will send an EnvChange that // will cause the zombie, but only if we actually go to the wire; // Sphinx and Shiloh won't send the env change, so we have to handle // them ourselves. Zombie(); } } } internal void Commit() { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#", ObjectID); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { // If this transaction has been completed, throw exception since it is unusable. try { // COMMIT ignores transaction names, and so there is no reason to pass it anything. COMMIT // simply commits the transaction from the most recent BEGIN, nested or otherwise. _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, null, false); // SQL BU DT 291159 - perform full Zombie on pre-Yukon, but do not actually // complete internal transaction until informed by server in the case of Yukon // or later. if (!IsZombied && !_innerConnection.IsYukonOrNewer) { // Since nested transactions are no longer allowed, set flag to false. // This transaction has been completed. Zombie(); } else { ZombieParent(); } } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Completed(TransactionState transactionState) { Debug.Assert (TransactionState.Active < transactionState, "invalid transaction completion state?"); _transactionState = transactionState; Zombie(); } internal Int32 DecrementAndObtainOpenResultCount() { Int32 openResultCount = Interlocked.Decrement(ref _openResultCount); if (openResultCount < 0) { throw ADP.InvalidOperation("Internal Error: Open Result Count Exceeded"); } return openResultCount; } internal void Dispose() { this.Dispose(true); System.GC.SuppressFinalize(this); } private /*protected override*/ void Dispose(bool disposing) { Bid.PoolerTrace(" %d#, Disposing\n", ObjectID); if (disposing) { if (null != _innerConnection) { // implicitly rollback if transaction still valid _disposing = true; this.Rollback(); } } } private int GetServerTransactionLevel() { // This function is needed for those times when it is impossible to determine the server's // transaction level, unless the user's arguments were parsed - which is something we don't want // to do. An example when it is impossible to determine the level is after a rollback. // using (SqlCommand transactionLevelCommand = new SqlCommand("set @out = @@trancount", (SqlConnection)(_innerConnection.Owner))) { transactionLevelCommand.Transaction = Parent; SqlParameter parameter = new SqlParameter("@out", SqlDbType.Int); parameter.Direction = ParameterDirection.Output; transactionLevelCommand.Parameters.Add(parameter); // transactionLevelCommand.RunExecuteReader(0, RunBehavior.UntilDone, false /* returnDataStream */, ADP.GetServerTransactionLevel); return (int)parameter.Value; } } internal Int32 IncrementAndObtainOpenResultCount() { Int32 openResultCount = Interlocked.Increment(ref _openResultCount); if (openResultCount < 0) { throw ADP.InvalidOperation("Internal Error: Open Result Count Exceeded"); } return openResultCount; } internal void InitParent(SqlTransaction transaction) { Debug.Assert(_parent == null, "Why do we have a parent on InitParent?"); _parent = new WeakReference(transaction); } internal void Rollback() { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#", ObjectID); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { try { // If no arg is given to ROLLBACK it will rollback to the outermost begin - rolling back // all nested transactions as well as the outermost transaction. _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); // Since Rollback will rollback to outermost begin, no need to check // server transaction level. This transaction has been completed. Zombie(); } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); if (!_disposing) { throw; } } else { throw; } } } finally { Bid.ScopeLeave(ref hscp); } } internal void Rollback(string transactionName) { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, transactionName='%ls'", ObjectID, transactionName); if (_innerConnection.IsLockedForBulkCopy) { throw SQL.ConnectionLockedForBcpEvent(); } _innerConnection.ValidateConnectionForExecute(null); try { // ROLLBACK takes either a save point name or a transaction name. It will rollback the // transaction to either the save point with the save point name or begin with the // transacttion name. NOTE: for simplicity it is possible to give all save point names // the same name, and ROLLBACK will simply rollback to the most recent save point with the // save point name. if (ADP.IsEmpty(transactionName)) throw SQL.NullEmptyTransactionName(); try { _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, transactionName, IsolationLevel.Unspecified, null, false); if (!IsZombied && !_innerConnection.IsYukonOrNewer) { // Check if Zombied before making round-trip to server. // Against Yukon we receive an envchange on the ExecuteTransaction above on the // parser that calls back into SqlTransaction for the Zombie() call. CheckTransactionLevelAndZombie(); } } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Save(string savePointName) { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, savePointName='%ls'", ObjectID, savePointName); _innerConnection.ValidateConnectionForExecute(null); try { // ROLLBACK takes either a save point name or a transaction name. It will rollback the // transaction to either the save point with the save point name or begin with the // transacttion name. So, to rollback a nested transaction you must have a save point. // SAVE TRANSACTION MUST HAVE AN ARGUMENT!!! Save Transaction without an arg throws an // exception from the server. So, an overload for SaveTransaction without an arg doesn't make // sense to have. Save Transaction does not affect the transaction level. if (ADP.IsEmpty(savePointName)) throw SQL.NullEmptyTransactionName(); try { _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Save, savePointName, IsolationLevel.Unspecified, null, false); } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { CheckTransactionLevelAndZombie(); } throw; } } finally { Bid.ScopeLeave(ref hscp); } } internal void Zombie() { // Called by several places in the code to ensure that the outer // transaction object has been zombied and the parser has broken // it's reference to us. // NOTE: we'll be called from the TdsParser when it gets appropriate // ENVCHANGE events that indicate the transaction has completed, however // we cannot rely upon those events occuring in the case of pre-Yukon // servers (and when we don't go to the wire because the connection // is broken) so we can also be called from the Commit/Rollback/Save // methods to handle that case as well. // There are two parts to a full zombie: // 1) Zombie parent and disconnect outer transaction from internal transaction // 2) Disconnect internal transaction from connection and parser // Number 1 needs to be done whenever a SqlTransaction object is completed. Number // 2 is only done when a transaction is actually completed. Since users can begin // transactions both in and outside of the API, and since nested begins are not actual // transactions we need to distinguish between #1 and #2. See SQL BU DT 291159 // for further details. ZombieParent(); SqlInternalConnection innerConnection = _innerConnection; _innerConnection = null; if (null != innerConnection) { innerConnection.DisconnectTransaction(this); } } private void ZombieParent() { if (null != _parent) { SqlTransaction parent = (SqlTransaction) _parent.Target; if (null != parent) { parent.Zombie(); } _parent = null; } } internal string TraceString() { return String.Format(/*IFormatProvider*/ null, "(ObjId={0}, tranId={1}, state={2}, type={3}, open={4}, disp={5}", ObjectID, _transactionId, _transactionState, _transactionType, _openResultCount, _disposing); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ReferentialConstraint.cs
- HelpProvider.cs
- TableProviderWrapper.cs
- StateMachine.cs
- BamlRecordHelper.cs
- ObjectContext.cs
- GradientSpreadMethodValidation.cs
- LinkTarget.cs
- QueueProcessor.cs
- RoutedEventArgs.cs
- PropertyTabAttribute.cs
- RSAOAEPKeyExchangeDeformatter.cs
- ServiceSecurityContext.cs
- EntityDataSourceEntityTypeFilterItem.cs
- ListSortDescriptionCollection.cs
- TerminatorSinks.cs
- SoapIgnoreAttribute.cs
- BaseProcessor.cs
- DelimitedListTraceListener.cs
- ParserExtension.cs
- PenLineJoinValidation.cs
- InlineCollection.cs
- FileDataSourceCache.cs
- TCPListener.cs
- SplineKeyFrames.cs
- Int16AnimationUsingKeyFrames.cs
- SystemInformation.cs
- TextEffectCollection.cs
- TimeSpanSecondsConverter.cs
- RegistryConfigurationProvider.cs
- UrlMappingsModule.cs
- SchemaElement.cs
- BookmarkManager.cs
- RijndaelManaged.cs
- _NestedSingleAsyncResult.cs
- SqlNotificationEventArgs.cs
- WebConfigurationManager.cs
- GridViewColumnCollectionChangedEventArgs.cs
- ExternalException.cs
- DefaultHttpHandler.cs
- WindowsRegion.cs
- ToolboxItemLoader.cs
- SafeTimerHandle.cs
- SqlRetyper.cs
- RbTree.cs
- SingleAnimation.cs
- RectangleF.cs
- OracleConnectionStringBuilder.cs
- WinInet.cs
- DataAccessor.cs
- QuaternionAnimation.cs
- AsyncOperation.cs
- EventSinkHelperWriter.cs
- SafeFileMappingHandle.cs
- XmlILAnnotation.cs
- ToolStripContentPanel.cs
- AssemblyResourceLoader.cs
- Console.cs
- CheckPair.cs
- DrawingBrush.cs
- DataGridCell.cs
- AudioFormatConverter.cs
- TextLine.cs
- QuestionEventArgs.cs
- HtmlElement.cs
- MarshalByValueComponent.cs
- TextLineBreak.cs
- WeakEventTable.cs
- MasterPageBuildProvider.cs
- SourceItem.cs
- DesignerDataConnection.cs
- ExtensibleClassFactory.cs
- NativeMethods.cs
- JournalEntryListConverter.cs
- IndexedEnumerable.cs
- XPathSelfQuery.cs
- EntityTypeBase.cs
- Set.cs
- SerializationInfo.cs
- TextParaClient.cs
- SamlAssertion.cs
- DataGridViewCellStyleChangedEventArgs.cs
- TableColumn.cs
- ValidationErrorCollection.cs
- Win32.cs
- RelationshipEndMember.cs
- UnsettableComboBox.cs
- OdbcError.cs
- OracleCommandBuilder.cs
- WebPartManagerInternals.cs
- SQLDateTimeStorage.cs
- Polygon.cs
- DispatcherSynchronizationContext.cs
- TypeDescriptorContext.cs
- Point3DCollectionValueSerializer.cs
- ConfigurationStrings.cs
- ellipse.cs
- X500Name.cs
- DispatcherTimer.cs
- EncryptRequest.cs