Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Data / System / Data / SqlClient / sqlinternaltransaction.cs / 1305376 / 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 { TdsParser.ReliabilitySection.Assert("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.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- IApplicationTrustManager.cs
- NotEqual.cs
- RankException.cs
- SQLInt64.cs
- ToolStripItemImageRenderEventArgs.cs
- OutputCacheModule.cs
- DtcInterfaces.cs
- ByteStack.cs
- UnmanagedMarshal.cs
- FlowDocument.cs
- AutoResetEvent.cs
- DataDocumentXPathNavigator.cs
- Int16.cs
- WebBrowserSiteBase.cs
- LightweightEntityWrapper.cs
- GeneralTransform.cs
- InstanceLockedException.cs
- PEFileEvidenceFactory.cs
- BevelBitmapEffect.cs
- CustomCategoryAttribute.cs
- UnsafeNativeMethods.cs
- SplitterCancelEvent.cs
- ErrorHandlingReceiver.cs
- PromptBuilder.cs
- DoubleAnimationClockResource.cs
- ToolStripLocationCancelEventArgs.cs
- SecureEnvironment.cs
- UnsafeMethods.cs
- MemoryStream.cs
- Label.cs
- ServiceHttpModule.cs
- TypeSystemProvider.cs
- PostBackTrigger.cs
- InkCanvasFeedbackAdorner.cs
- RtfControlWordInfo.cs
- Help.cs
- SelectionRange.cs
- TextServicesLoader.cs
- DataGridViewRowContextMenuStripNeededEventArgs.cs
- DataGridViewButtonCell.cs
- WebServiceMethodData.cs
- VerificationAttribute.cs
- MdiWindowListItemConverter.cs
- KnownTypeAttribute.cs
- CollectionViewGroup.cs
- WmpBitmapDecoder.cs
- ActivityCollectionMarkupSerializer.cs
- SqlColumnizer.cs
- EdmMember.cs
- NumberFunctions.cs
- ProjectedSlot.cs
- Converter.cs
- ToolStripSeparatorRenderEventArgs.cs
- ComEventsInfo.cs
- SqlEnums.cs
- ListItemParagraph.cs
- IndexingContentUnit.cs
- ObjectViewEntityCollectionData.cs
- DataBinder.cs
- ScrollViewerAutomationPeer.cs
- BaseCodeDomTreeGenerator.cs
- AdornerPresentationContext.cs
- CatalogPartCollection.cs
- Aggregates.cs
- HtmlGenericControl.cs
- HandleTable.cs
- Emitter.cs
- AttachedAnnotation.cs
- StaticExtensionConverter.cs
- SupportsEventValidationAttribute.cs
- Exception.cs
- BindingsCollection.cs
- BinaryUtilClasses.cs
- EntityParameter.cs
- SettingsAttributeDictionary.cs
- CodeChecksumPragma.cs
- EmulateRecognizeCompletedEventArgs.cs
- DataGridRelationshipRow.cs
- AsynchronousChannelMergeEnumerator.cs
- GroupQuery.cs
- CustomErrorsSectionWrapper.cs
- ResourceSet.cs
- Canvas.cs
- PropertyTabAttribute.cs
- BaseResourcesBuildProvider.cs
- odbcmetadatafactory.cs
- FixedTextSelectionProcessor.cs
- InputScopeConverter.cs
- ShapeTypeface.cs
- Container.cs
- PermissionAttributes.cs
- _ConnectionGroup.cs
- Command.cs
- ReflectionTypeLoadException.cs
- ListViewInsertEventArgs.cs
- SafeRightsManagementPubHandle.cs
- SqlConnectionHelper.cs
- ProviderCommandInfoUtils.cs
- DataViewSettingCollection.cs
- Binding.cs