/// <summary> /// Executes the specified operation inside the current transaction context. If there is no context, a new one will be created with a <see cref="TransactionContextAffinity.NotSupported"/> affinity. /// </summary> /// <typeparam name="TState">The type of the data session state.</typeparam> /// <typeparam name="TResult">The type of the operation result.</typeparam> /// <param name="transactionGroupName">The transaction group name.</param> /// <param name="begin">The action to execute when the transaction context begins (before any operation).</param> /// <param name="commit">The action to execute when committing the transaction context.</param> /// <param name="rollback">The action to execute when rollbacking the transaction context.</param> /// <param name="end">The action to execute when the transaction context ends (after all operations).</param> /// <param name="operation">The operation to execute inside the current transaction context.</param> /// <returns>The result of the operation.</returns> public static async Task <TResult> Execute <TState, TResult>(string transactionGroupName, Func <TransactionContext, Task <TState> > begin, Func <DataSession <TState>, Task> commit, Func <DataSession <TState>, Task> rollback, Func <DataSession <TState>, Task> end, Func <DataSession <TState>, Task <TResult> > operation) { if (string.IsNullOrEmpty(transactionGroupName)) { throw new ArgumentNullException(nameof(transactionGroupName)); } if (begin == null) { throw new ArgumentNullException(nameof(begin)); } if (commit == null) { throw new ArgumentNullException(nameof(commit)); } if (rollback == null) { throw new ArgumentNullException(nameof(rollback)); } if (end == null) { throw new ArgumentNullException(nameof(end)); } if (operation == null) { throw new ArgumentNullException(nameof(operation)); } var current = TransactionContext.CurrentTransactionContext; bool ownerOfCurrent = false; if (current == null) { current = new TransactionContext(TransactionContextAffinity.NotSupported); ownerOfCurrent = true; } if (current.State == TransactionContextState.Exited) { throw new InvalidOperationException(string.Format(Resources.ExceptionMessages.Transactions_InvalidTransactionContextState, string.Join(",", new string[] { TransactionContextState.Entered.ToString(), TransactionContextState.ToBeCommitted.ToString(), TransactionContextState.ToBeRollbacked.ToString() }))); } if (current.Affinity == TransactionContextAffinity.NotSupported) { try { var state = await begin(current).ConfigureAwait(false); var session = new DataSession <TState>(current, transactionGroupName, state, commit, rollback, end); try { var result = await operation(session).ConfigureAwait(false); if (ownerOfCurrent) { current.VoteCommit(); } return(result); } finally { await end(session).ConfigureAwait(false); } } finally { if (ownerOfCurrent && current != null) { current.Exit(); } } } else { var controller = current.GetController(); Debug.Assert(controller != null); var sessionsByTransactionGroup = _pending.GetOrAdd(controller, new ConcurrentDictionary <string, Lazy <Task <DataSession> > >()); var session = await sessionsByTransactionGroup.GetOrAdd(transactionGroupName, new Lazy <Task <DataSession> >( async() => { // begin the session here so that it is guaranteed to run only once var state = await begin(current).ConfigureAwait(false); return(new DataSession <TState>(current, transactionGroupName, state, commit, rollback, end)); }, LazyThreadSafetyMode.ExecutionAndPublication)).Value.ConfigureAwait(false); return(await operation((DataSession <TState>) session).ConfigureAwait(false)); } }
private Task RollbackSession(DataSession<DataSessionState> session) { if (session == null) throw new ArgumentNullException(nameof(session)); return Task.Run( () => { var state = session.State; WriteOperationTrace(state, "rollbackSession"); WriteOperationTrace(state, "rollbackTransaction"); // catch any further error from DB // in particular the case where the transaction has already been aborted by the DB itself try { state.Transaction.Rollback(); } catch (DbException ex) { Trace.WriteLine(ex, "NLight"); } }); }
private Task EndSession(DataSession<DataSessionState> session) { if (session == null) throw new ArgumentNullException(nameof(session)); return Task.Run( () => { var state = session.State; WriteOperationTrace(state, "endSession"); if (!state.ConnectionClosedByReader) { WriteOperationTrace(state, "closeConnection"); state.Connection.Close(); state.Connection.Dispose(); } }); }
private Task CommitSession(DataSession<DataSessionState> session) { if (session == null) throw new ArgumentNullException(nameof(session)); return Task.Run( () => { var state = session.State; WriteOperationTrace(state, "commitSession"); WriteOperationTrace(state, "commitTransaction"); state.Transaction.Commit(); }); }