| File: TransactionalAction.cs | Web Access |
| Project: src\sdk\src\Cli\dotnet\dotnet.csproj (dotnet) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable disable using System.Reflection; using System.Transactions; using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli; public sealed class TransactionalAction { static TransactionalAction() { DisableTransactionTimeoutUpperLimit(); } private class EnlistmentNotification(Action commit, Action rollback) : IEnlistmentNotification { private Action _commit = commit; private Action _rollback = rollback; public void Commit(Enlistment enlistment) { if (_commit != null) { _commit(); _commit = null; } enlistment.Done(); } public void InDoubt(Enlistment enlistment) { Rollback(enlistment); } public void Prepare(PreparingEnlistment enlistment) { enlistment.Prepared(); } public void Rollback(Enlistment enlistment) { if (_rollback != null) { _rollback(); _rollback = null; } enlistment.Done(); } } public static T Run<T>( Func<T> action, Action commit = null, Action rollback = null) { if (action == null) { throw new ArgumentNullException(nameof(action)); } // This automatically inherits any ambient transaction // If a transaction is inherited, completing this scope will be a no-op T result = default(T); try { using (var scope = new TransactionScope( TransactionScopeOption.Required, TimeSpan.Zero)) { Transaction.Current.EnlistVolatile( new EnlistmentNotification(commit, rollback), EnlistmentOptions.None); result = action(); scope.Complete(); } return result; } catch (TransactionAbortedException ex) { Reporter.Verbose.WriteLine(string.Format("TransactionAbortedException Message: {0}", ex.Message)); Reporter.Verbose.WriteLine( $"Inner Exception Message: {ex?.InnerException?.Message + "---" + ex?.InnerException}"); throw; } } private static void SetTransactionManagerField(string fieldName, object value) { typeof(TransactionManager).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static) .SetValue(null, value); } // https://github.com/dotnet/sdk/issues/21101 // we should use the proper API once it is available public static void DisableTransactionTimeoutUpperLimit() { SetTransactionManagerField("s_cachedMaxTimeout", true); SetTransactionManagerField("s_maximumTimeout", TimeSpan.Zero); } public static void Run( Action action, Action commit = null, Action rollback = null) { Run<object>( action: () => { action(); return null; }, commit: commit, rollback: rollback); } }