/// <inheritdoc/>
        public ITransaction CreateTransaction()
        {
            lock (lockObject)
            {
                if (RollInProgress)
                    throw new TransactionException("Unable to create a transaction. A rollback or rollforward operation is in progress.");

                var transaction = new Transaction(this);
                transactionsInProgress.Push(transaction);
                TransactionInProgress = true;
                return transaction;
            }
        }
        /// <inheritdoc/>
        public void CompleteTransaction(Transaction transaction)
        {
            lock (lockObject)
            {
                try
                {
                    if (transactionsInProgress.Count == 0)
                        throw new TransactionException("There is not transaction in progress in the transaction stack.");

                    if (transaction != transactionsInProgress.Pop())
                        throw new TransactionException("The transaction being completed is not that last created transaction.");

                    // Check if we're completing the last transaction
                    TransactionInProgress = transactionsInProgress.Count > 0;

                    // Ignore the transaction if it is empty
                    if (transaction.IsEmpty)
                        return;

                    // If this transaction has no effect, discard it.
                    if (transaction.Operations.All(x => !x.HasEffect))
                        return;

                    // If we're not the last transaction, consider this transaction as an operation of its parent transaction
                    if (TransactionInProgress)
                    {
                        // Avoid useless nested transaction if we have a single operation inside.
                        PushOperation(transaction.Operations.Count == 1 ? transaction.Operations.Single() : transaction);
                        return;
                    }

                    // Remove transactions that will be overwritten by this one
                    if (currentPosition < transactions.Count)
                    {
                        PurgeFromIndex(currentPosition);
                    }

                    if (currentPosition == Capacity)
                    {
                        // If the stack has a capacity of 0, immediately freeze the new transaction.
                        var oldestTransaction = Capacity > 0 ? transactions[0] : transaction;
                        oldestTransaction.Interface.Freeze();

                        for (var i = 1; i < transactions.Count; ++i)
                        {
                            transactions[i - 1] = transactions[i];
                        }
                        if (Capacity > 0)
                        {
                            transactions[--currentPosition] = null;
                        }
                        TransactionDiscarded?.Invoke(this, new TransactionsDiscardedEventArgs(oldestTransaction, DiscardReason.StackFull));
                    }
                    if (Capacity > 0)
                    {
                        if (currentPosition == transactions.Count)
                        {
                            transactions.Add(transaction);
                        }
                        else
                        {
                            transactions[currentPosition] = transaction;
                        }
                        ++currentPosition;
                    }
                }
                finally
                {
                    if (!TransactionInProgress)
                    {
                        TransactionCompleted?.Invoke(this, new TransactionEventArgs(transaction));
                    }
                }
            }
        }