/// <summary> /// Create a Task based on Begin/End IAsyncResult pattern. /// </summary> /// <param name="transaction">The transaction (optional) to use. If not null a TransactionScope will be used when calling the begin Func.</param> /// <param name="begin"></param> /// <param name="end"></param> /// <param name="state"> /// This parameter helps reduce allocations by passing state to the Funcs. e.g.: /// await TaskHelpers.CreateTask( /// (c, s) => ((Transaction)s).BeginCommit(c, s), /// (a) => ((Transaction)a.AsyncState).EndCommit(a), /// transaction); /// </param> public static Task CreateTransactionalTask(Transaction transaction, Func <AsyncCallback, object, IAsyncResult> begin, Action <IAsyncResult> end, object state = null) { Task retval; TransactionScope scope = null; try { scope = Fx.CreateTransactionScope(transaction); retval = Task.Factory.FromAsync(begin, end, state); Fx.CompleteTransactionScope(ref scope); } catch (Exception ex) { if (Fx.IsFatal(ex)) { throw; } if (scope != null) { scope.Dispose(); } var completionSource = new TaskCompletionSource <object>(state); completionSource.SetException(ex); retval = completionSource.Task; } return(retval); }
protected virtual void Dispose(bool disposing) { if (disposing && !this.disposed) { this.disposed = true; if (this.State == TransactionSignalState.Ready) { this.State = TransactionSignalState.Abandoned; } else if (this.State == TransactionSignalState.Prepared) { this.State = TransactionSignalState.Completed; } else { AsyncResult.ThrowInvalidAsyncResult("PrepareTransactionalCall should only be called in a using. Dispose called multiple times."); } try { Fx.CompleteTransactionScope(ref this.transactionScope); } catch (Exception exception) { if (Fx.IsFatal(exception)) { throw; } // Complete and Dispose are not expected to throw. If they do it can mess up the AsyncResult state machine. throw Fx.Exception.AsError(new InvalidOperationException(CommonResources.AsyncTransactionException)); } // This will release the callback to run, or tell us that we need to defer the callback to Check/SyncContinue. // // It's possible to avoid this Interlocked when CompletedSynchronously is true, but we have no way of knowing that // from here, and adding a way would add complexity to the AsyncResult transactional calling pattern. This // unnecessary Interlocked only happens when: PrepareTransactionalCall is called with a non-null transaction, // PrepareAsyncCompletion is reached, and the operation completes synchronously or with an exception. IAsyncResult result; if (this.State == TransactionSignalState.Completed && Unlock(out result)) { if (this.parent.deferredTransactionalResult != null) { AsyncResult.ThrowInvalidAsyncResult(this.parent.deferredTransactionalResult); } this.parent.deferredTransactionalResult = result; } } }