The same question has been asked for Java, but I'm interested in a .NET answer.
Consider the following code:
class Program
{
static void Main()
{
try
{
RunTransaction();
// If there was an exception within the transaction,
// I won't be here anymore. But if the transaction was
// cancelled without an exception being thrown, I really
// need to know because I must stop here anyway.
OtherCode();
}
catch (Excexption ex)
{
// Log the exception...
// If an exception was thrown in the transaction scope,
// this must be logged here. If a "helper" exception was
// created in the Dispose method, this may be logged, but
// it won't say much. It just made sure that nothing else
// was executed in this try block.
}
}
static void RunTransaction()
{
using (var trans = new Transaction())
{
// An error may occur here and it should be logged.
throw new Exception();
// Maybe the scope is simply left without an exception.
return;
// Otherwise, the transaction is committed.
trans.Commit();
}
}
}
class Transaction : IDisposable
{
bool isCommitted;
public void Commit()
{
isCommitted = true;
}
public void Dispose()
{
if (!isCommitted)
{
// Was an exception thrown before this is called?
// If not, I might consider throwing one here.
// I can't always throw an exception here because if
// another exception is already propagated, it would
// be dropped and the real error cause would not be
// visible anymore.
}
}
}
In the Transaction.Dispose
method, how can I know whether an exception was already thrown?
Note that the finally
block is not explicitly shown here, but hidden in the using
statement which calls the IDisposable.Dispose
method, which is shown here.
Update: My background is that I have a transaction wrapper class that behaves a bit like TransactionScope
. But TransactionScope
is too much magic and doesn't work as expected so I went back to real database transactions. Some methods need a transaction but if they're called from another method that already needed a transaction, the inner "transaction" must "join" the outer transaction instead of requesting a new, nested transaction from the database, which is not supported anywhere I know of. The real code is a bit more complex than my sample, where the inner transaction may be cancelled, effectively ending the transaction. Then, if anything continues to run in the outer transaction, which does not exist anymore, it cannot be rolled back, but will effectively run outside of any transaction! This must be prevented by all means. Thoring an exception in the first place would do it, but the inner transaction can also be cancelled without that. This is what I want to detect in my scope helper class.