Example #1
0
        /// <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));
            }
        }
Example #2
0
		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");
					}
				});
		}
Example #3
0
		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();
					}
				});
		}
Example #4
0
		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();
				});
		}