|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
namespace Microsoft.DotNet.Cli;
internal interface ITransactionContext
{
public void AddRollbackAction(Action action);
// Add an action which will be run at the end of the transaction, whether it succeeds or not (after any rollback actions, if applicable)
public void AddCleanupAction(Action action);
}
internal class CliTransaction
{
// Delegate called when rollback starts, mainly in order to print a log message
public Action RollbackStarted { get; set; }
// Delegate called when rollback fails. If set, exception will be passed to the delegate, but not rethrown
public Action<Exception> RollbackFailed { get; set; }
public static void RunNew(Action<ITransactionContext> action)
{
new CliTransaction().Run(action);
}
public void Run(Action<ITransactionContext> action, Action rollback)
{
Run(context =>
{
context.AddRollbackAction(rollback);
action(context);
});
}
public void Run(Action<ITransactionContext> action)
{
TransactionContext transactionContext = new();
try
{
action(transactionContext);
}
catch (Exception)
{
// Roll back transaction
RollbackStarted?.Invoke();
transactionContext.RollbackActions.Reverse();
foreach (var rollbackAction in transactionContext.RollbackActions)
{
try
{
rollbackAction();
}
catch (Exception ex)
{
if (RollbackFailed != null)
{
RollbackFailed(ex);
}
else
{
throw;
}
}
}
throw;
}
finally
{
foreach (var cleanupAction in transactionContext.CleanupActions)
{
cleanupAction();
}
}
}
class TransactionContext : ITransactionContext
{
public List<Action> RollbackActions { get; set; } = [];
// Actions which will be run either when the transaction completes successfully, or after rollback actions have been run
public List<Action> CleanupActions { get; set; } = [];
public void AddRollbackAction(Action action)
{
RollbackActions.Add(action);
}
public void AddCleanupAction(Action action)
{
CleanupActions.Add(action);
}
}
}
static class CliTransactionExtensions
{
public static void Run(this ITransactionContext context, Action action, Action rollback = null, Action cleanup = null)
{
if (rollback != null)
{
context.AddRollbackAction(rollback);
}
if (cleanup != null)
{
context.AddCleanupAction(cleanup);
}
action();
}
}
|