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
- CommentEmitter.cs
- PageContentCollection.cs
- BCLDebug.cs
- DebugHandleTracker.cs
- Italic.cs
- NestedContainer.cs
- PieceNameHelper.cs
- TempFiles.cs
- StrongNameIdentityPermission.cs
- CodePrimitiveExpression.cs
- metrodevice.cs
- XamlPathDataSerializer.cs
- AuthenticationModuleElement.cs
- Version.cs
- MasterPageBuildProvider.cs
- ClientProxyGenerator.cs
- FileDialogCustomPlace.cs
- InputMethodStateTypeInfo.cs
- WorkflowServiceNamespace.cs
- Label.cs
- XmlName.cs
- PackageProperties.cs
- ButtonChrome.cs
- WebPartVerb.cs
- MediaPlayerState.cs
- UserInitiatedNavigationPermission.cs
- PropertyDescriptors.cs
- DesignerToolStripControlHost.cs
- InputLangChangeEvent.cs
- EntitySet.cs
- ImportContext.cs
- GroupBox.cs
- RelationalExpressions.cs
- WebBrowserNavigatingEventHandler.cs
- CompiledRegexRunner.cs
- TreeWalker.cs
- ContactManager.cs
- BinaryHeap.cs
- ToolStripGrip.cs
- ReaderOutput.cs
- WebPartCatalogCloseVerb.cs
- ControlBuilder.cs
- LicFileLicenseProvider.cs
- RetrieveVirtualItemEventArgs.cs
- CustomErrorsSectionWrapper.cs
- GestureRecognizer.cs
- Sequence.cs
- TextCompositionManager.cs
- RSAPKCS1SignatureDeformatter.cs
- NativeWindow.cs
- XmlAnyElementAttribute.cs
- DecimalAnimationBase.cs
- TextElementCollectionHelper.cs
- XmlDocumentFieldSchema.cs
- RadioButtonAutomationPeer.cs
- DataSourceHelper.cs
- WinInetCache.cs
- SyndicationLink.cs
- HostingPreferredMapPath.cs
- RpcAsyncResult.cs
- AutoResizedEvent.cs
- FixedSOMLineRanges.cs
- XmlTextEncoder.cs
- MdiWindowListStrip.cs
- DataGridViewCellStyleChangedEventArgs.cs
- SBCSCodePageEncoding.cs
- XhtmlBasicValidatorAdapter.cs
- BrowserCapabilitiesFactory.cs
- TypedElement.cs
- TdsEnums.cs
- TypeResolver.cs
- TypeSemantics.cs
- Message.cs
- Double.cs
- WebHeaderCollection.cs
- IPPacketInformation.cs
- PersonalizationProviderHelper.cs
- ReaderWriterLock.cs
- LicFileLicenseProvider.cs
- IDataContractSurrogate.cs
- XsdBuilder.cs
- FileVersion.cs
- AspProxy.cs
- Block.cs
- WebZone.cs
- CodeMemberProperty.cs
- _ListenerResponseStream.cs
- WaitHandleCannotBeOpenedException.cs
- LockCookie.cs
- DataGridViewCellFormattingEventArgs.cs
- BinaryNode.cs
- ResourceDescriptionAttribute.cs
- InvalidateEvent.cs
- SemaphoreFullException.cs
- WebPartVerb.cs
- SqlDataSourceConfigureSelectPanel.cs
- EntityDataSourceView.cs
- MatrixConverter.cs
- FilterableAttribute.cs
- FrameworkReadOnlyPropertyMetadata.cs