In TIBCO Spotfire (I have no control over their code), there is an abstract class - DocumentNode which has a property Transactions implemented like so (according to disassembly):
public ITransactions Transactions
{
[ApiVersion("2.0")] get => (ITransactions) this;
}
So then DocumentNode implements
public interface ITransactions
{
AggregatedTransactionHandle BeginAggregatedTransaction();
void ExecuteInvisibleTransaction(Executor executor);
void ExecuteStickyTransaction(Guid guid, Executor executor);
void ExecuteTransaction( Executor executor);
}
where Executor is
delegate void Executor()
DocumentNode (again according to disassembly) implements the interface like so:
[ApiVersion("2.0")]
void ITransactions.ExecuteTransaction(Executor executor) => this.Transaction("Anonymous transaction", executor);
[ApiVersion("2.0")]
void ITransactions.ExecuteInvisibleTransaction(Executor executor) => this.InvisibleTransaction(executor);
[ApiVersion("2.0")]
void ITransactions.ExecuteStickyTransaction(Guid guid, Executor executor) => this.StickyTransaction(guid, executor);
[ApiVersion("2.0")]
public INodeContext Context
{
[ApiVersion("2.0")] get => (INodeContext) this;
}
[ApiVersion("2.0")]
public ITransactions Transactions
{
[ApiVersion("2.0")] get => (ITransactions) this;
}
But is only exposed via Transactions property and not directly via DocumentNode. The code I have SOME control over has many classes that inherit from DocumentNode and use ExecuteTransaction excessively causing nested transactions. Typical use:
this.Transactions.ExecuteTransaction(...
Internally Spotfire has some rules about what state an outer transaction can be in when inner one starts and it throws exceptions when the state does not match. I can't access that internal state, but I would be satisfied with a solution where if such Exception is caught, we run the executor without starting a transaction. I also need to keep the code changes to a minimum (I just do).
So I plan to make some of the classes that inherit from DocumentNode to instead inherit from DocumentNodeExt
public class DocumentNodeExt : DocumentNode
{
public DocumentNodeExt() : base()
{ }
public DocumentNodeExt(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
public new ITransactions Transactions
{
get { return this; }
}
public AggregatedTransactionHandle BeginAggregatedTransaction()
{
return base.Transactions.BeginAggregatedTransaction(); // stack overflow
// return ((DocumentNode)this).Transactions.BeginAggregatedTransaction(); // stack overflow
}
public void ExecuteInvisibleTransaction(Executor executor)
{
try
{
base.Transactions.ExecuteInvisibleTransaction(executor);
}
catch (InvalidOperationException)
{
executor();
}
}
public void ExecuteStickyTransaction(Guid guid, Executor executor)
{
base.Transactions.ExecuteStickyTransaction( guid, executor);
}
void ExecuteTransaction(Executor executor)
{
try
{
base.Transactions.ExecuteTransaction(executor);
}
catch (InvalidOperationException)
{
executor();
}
}
}
And here is the problem: since DocumentNode.Transactions is not virtual, I can't override it in the derived class. I can only hide it via "new", but then how do I call DocumentNode's implementation of ExecuteTransaction? base does not have a method ExecuteTransaction and base.Transactions is the same as this.Transactions.
I could forget hiding Transactions and simply implement the ITransactions, but then I would also need to change the typical calls from
this.Transactions.ExecuteTransaction(...
to
this.ExecuteTransaction(...
which is more code changes than my "quota" allows.