Q:

Is it possible to detect if an exception occurred before I entered a finally block in .NET?

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.

A:

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.
    }
}

You say you want to throw an exception from Dispose if one wasn't thrown already.
But Dispose should not throw exceptions. From Implementing a Dispose method:

To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.

Also from Dispose Pattern:

AVOID throwing an exception from within Dispose(bool) except under critical situations where the containing process has been corrupted (leaks, inconsistent shared state, etc.).
Users expect that a call to Dispose will not raise an exception.
If Dispose could raise an exception, further finally-block cleanup logic will not execute. To work around this, the user would need to wrap every call to Dispose (within the finally block!) in a try block, which leads to very complex cleanup handlers. If executing a Dispose(bool disposing) method, never throw an exception if disposing is false. Doing so will terminate the process if executing inside a finalizer context.

