internal DistributedTransaction PSPEPromote(InternalTransaction tx)
{
bool changeToReturnState = true;
TransactionState returnState = tx.State;
Debug.Assert(returnState == TransactionStateDelegated ||
returnState == TransactionStateDelegatedSubordinate ||
returnState == TransactionStateDelegatedNonMSDTC,
"PSPEPromote called from state other than TransactionStateDelegated[NonMSDTC]");
CommonEnterState(tx);
DistributedTransaction distributedTx = null;
try
{
if (tx._attemptingPSPEPromote)
{
// There should not already be a PSPEPromote call outstanding.
throw TransactionException.CreateInvalidOperationException(
TraceSourceType.TraceSourceLtm,
SR.PromotedReturnedInvalidValue,
null,
tx.DistributedTxId
);
}
tx._attemptingPSPEPromote = true;
byte[] propagationToken = tx._promoter.Promote();
// If the PromoterType is NOT MSDTC, then we can't assume that the returned
// byte[] is an MSDTC propagation token and we can't create an DistributedTransaction from it.
if (tx._promoterType != TransactionInterop.PromoterTypeDtc)
{
if (propagationToken == null)
{
throw TransactionException.CreateInvalidOperationException(
TraceSourceType.TraceSourceLtm,
SR.PromotedReturnedInvalidValue,
null,
tx.DistributedTxId
);
}
tx.promotedToken = propagationToken;
return null;
}
// From this point forward, we know that the PromoterType is TransactionInterop.PromoterTypeDtc so we can
// treat the propagationToken as an MSDTC propagation token. If one was returned.
if (propagationToken == null)
{
// If the returned propagationToken is null AND the tx.PromotedTransaction is null, the promote failed.
// But if the PSPE promoter used PSPEPromoteAndConvertToEnlistDurable, tx.PromotedTransaction will NOT be null
// at this point and we just use tx.PromotedTransaction as distributedTx and we don't bother to change to the
// "return state" because the transaction is already in the state it needs to be in.
if (tx.PromotedTransaction == null)
{
// The PSPE has returned an invalid promoted transaction.
throw TransactionException.CreateInvalidOperationException(
TraceSourceType.TraceSourceLtm,
SR.PromotedReturnedInvalidValue,
null,
tx.DistributedTxId
);
}
// The transaction has already transitioned to TransactionStatePromoted, so we don't want
// to change the state to the "returnState" because TransactionStateDelegatedBase.EnterState, would
// try to promote the enlistments again.
changeToReturnState = false;
distributedTx = tx.PromotedTransaction;
}
// At this point, if we haven't yet set distributedTx, we need to get it using the returned
// propagation token. The PSPE promoter must NOT have used PSPEPromoteAndConvertToEnlistDurable.
if (distributedTx == null)
{
try
{
distributedTx = TransactionInterop.GetDistributedTransactionFromTransmitterPropagationToken(
propagationToken
);
}
catch (ArgumentException e)
{
// The PSPE has returned an invalid promoted transaction.
throw TransactionException.CreateInvalidOperationException(
TraceSourceType.TraceSourceLtm,
SR.PromotedReturnedInvalidValue,
e,
tx.DistributedTxId
);
}
if (TransactionManager.FindPromotedTransaction(distributedTx.Identifier) != null)
{
// If there is already a promoted transaction then someone has committed an error.
distributedTx.Dispose();
throw TransactionException.CreateInvalidOperationException(
TraceSourceType.TraceSourceLtm,
SR.PromotedTransactionExists,
null,
tx.DistributedTxId
);
}
}
}
finally
{
tx._attemptingPSPEPromote = false;
// If we get here and changeToReturnState is false, the PSPE enlistment must have requested that we
// promote and convert the enlistment to a durable enlistment
// (Transaction.PSPEPromoteAndConvertToEnlistDurable). In that case, the internal transaction is
// already in TransactionStatePromoted, so we don't want to put it BACK into TransactionStateDelegatedBase.
if (changeToReturnState)
{
returnState.CommonEnterState(tx);
}
}
return distributedTx;
}