Code:
/ FX-1434 / FX-1434 / 1.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
- MachineKeySection.cs
- ImageDrawing.cs
- XmlSchemaAttributeGroupRef.cs
- ClaimTypes.cs
- ErrorTableItemStyle.cs
- DmlSqlGenerator.cs
- OperationCanceledException.cs
- DEREncoding.cs
- DataExpression.cs
- Marshal.cs
- EventLogStatus.cs
- DataGridViewCellErrorTextNeededEventArgs.cs
- NativeWindow.cs
- MetabaseServerConfig.cs
- PropertyConverter.cs
- TargetFrameworkUtil.cs
- ByteRangeDownloader.cs
- ExpressionNormalizer.cs
- CssClassPropertyAttribute.cs
- securitycriticaldataformultiplegetandset.cs
- ThrowHelper.cs
- FormViewDeleteEventArgs.cs
- StrongNameKeyPair.cs
- ContentDisposition.cs
- WebBrowserProgressChangedEventHandler.cs
- ServiceProviders.cs
- ScriptReference.cs
- WindowsGraphicsWrapper.cs
- DocumentPageHost.cs
- TextModifierScope.cs
- MethodRental.cs
- WebScriptEnablingElement.cs
- CustomSignedXml.cs
- BufferedOutputStream.cs
- ExecutionContext.cs
- FixUp.cs
- _ReceiveMessageOverlappedAsyncResult.cs
- ClosableStream.cs
- EvidenceBase.cs
- PipelineModuleStepContainer.cs
- PeerNameRegistration.cs
- XmlMemberMapping.cs
- ScrollProperties.cs
- TableLayoutSettings.cs
- Util.cs
- AnnouncementInnerClient11.cs
- ObjectViewListener.cs
- CodeStatementCollection.cs
- TableParaClient.cs
- BuilderPropertyEntry.cs
- CompositeControl.cs
- SettingsPropertyIsReadOnlyException.cs
- TriggerBase.cs
- GlobalProxySelection.cs
- NativeMethods.cs
- PassportAuthenticationEventArgs.cs
- SelectionUIService.cs
- SafeTimerHandle.cs
- Context.cs
- FunctionQuery.cs
- InvalidateEvent.cs
- ConfigurationManager.cs
- XmlSchemaExporter.cs
- GradientBrush.cs
- GeneralTransform3DCollection.cs
- RuntimeVariableList.cs
- DebuggerAttributes.cs
- TreeIterator.cs
- PenLineCapValidation.cs
- figurelengthconverter.cs
- TreeViewImageKeyConverter.cs
- EntitySqlQueryCacheKey.cs
- XdrBuilder.cs
- ReadWriteSpinLock.cs
- X509SecurityTokenAuthenticator.cs
- XmlDictionary.cs
- FontStyle.cs
- SmtpReplyReaderFactory.cs
- RefType.cs
- ObjectStateManagerMetadata.cs
- DependencyPropertyChangedEventArgs.cs
- BuildManagerHost.cs
- SchemaTypeEmitter.cs
- PtsHost.cs
- PageBreakRecord.cs
- DataGridViewCellToolTipTextNeededEventArgs.cs
- WithParamAction.cs
- InputReport.cs
- CardSpacePolicyElement.cs
- DocumentPageTextView.cs
- SessionPageStateSection.cs
- AttributeCollection.cs
- Literal.cs
- SplitterEvent.cs
- QueryValue.cs
- SignatureGenerator.cs
- CallbackValidator.cs
- ComponentConverter.cs
- UnionCodeGroup.cs
- XPathExpr.cs