示例#1
0
        /// <summary>
        ///     Create new instance and start ambient KTM transaction if necessary
        /// </summary>
        /// <param name="repository">
        ///     Repository manager
        /// </param>
        /// <param name="disposeIfSlave">
        ///     Dispose transaction created in scope when disposing the scope even if it is part of the ambient managed transaction.
        ///     Has no effect if <paramref name="joinAmbientManaged"/> is <see langword="false"/>.
        /// </param>
        /// <param name="okToStartOwnTransaction">
        ///		Whether to allow staring own storage transaction (slave transaction is not owned).
        /// </param>
        /// <remarks>
        ///		DOCO: transaction settings
        ///		When wrapping existing transaction a priority is always given to storage transaction scope.
        ///		Settings allow prohibiting the use of any external transaction (AlwaysStartNew) or only managed (DisallowJoiningAmbientManaged).
        ///		You cannot prohibit usage of external storage transactions but allow that of external managed transactions.
        ///		Hence if AlwaysStartNew is OFF and there's an ambient storage transaction when the scope is created it will wrap around that
        ///		existing storage transaction.
        /// </remarks>
        protected StorageTransactionScope(IRepository repository, bool disposeIfSlave, bool okToStartOwnTransaction)
        {
            IStorageTransactionScope   scopeToWrap;
            IFileSystemProvider        fsProvider = repository.ObjectFactory.FileSystemProvider;
            StorageTransactionSettings settings   = repository.Settings.StorageTransactionSettings;

            bool noTransactionOption = (settings & StorageTransactionSettings.NoTransactions) == StorageTransactionSettings.NoTransactions;
            bool joinAmbientManaged  = (settings & StorageTransactionSettings.DisallowJoiningAmbientManaged) != StorageTransactionSettings.DisallowJoiningAmbientManaged;

            if (!noTransactionOption)
            {
                if ((settings & StorageTransactionSettings.RequireTransactions) == StorageTransactionSettings.RequireTransactions &&
                    !fsProvider.SupportsTransactions)
                {
                    // transactions required but not supported
                    throw new InvalidOperationException(StorageResources.TransactionsNotSupported);
                }

                // done: start new must only apply to top level transaction started in repository because if subsequent
                // nested methods create their scopes they would start their own transactions and would not see parent FS objects
                bool alwaysStartNewOption = IsAlwaysNewTransactionOptionOn(repository: repository);

                // note: "start new" should not create multiple transactions in nested calls INSIDE repository; only one at the top level
                // is all I need.
                bool startNewIfAlreadyActive = alwaysStartNewOption && !IsWithinStorageTransactionScope;

                // startNewIfAlreadyActive == true && joinAmbientManaged == true makes sense only when storage (KTM) transaction is already
                // active AND managed external transaction is also active but the KTM transaction is not part of the current Transaction.Current
                // because if it is then the result will be exactly the same as when just using already active KTM transaction

                if (!startNewIfAlreadyActive && fsProvider.IsStorageAmbientTransactionActive)
                {
                    // case for reusage of already existing ambient storage transaction; if there's ambient storage transaction and OK to reuse it
                    // (by not being required to start new one) then okToStartOwnTransaction is not relevant as I am not going to start own transaction
                    // anyway;
                    // "always start new" should prevent usage of external storage transactions; therefore when it is ON and not ok to start own tr-n
                    // I have to create null scope
                    _log.Debug("Ambient transaction active, returning preservation scope");
                    scopeToWrap = fsProvider.CreateTransactionScope(transactionToUse: fsProvider.AmbientTransaction, dispose: false);
                    Check.Assert(!scopeToWrap.ToBeDisposed && !scopeToWrap.HasChangedContext && fsProvider.AmbientTransaction == scopeToWrap.UnderlyingTransaction);
                }
                else
                {
                    if (joinAmbientManaged && IsManagedAmbientActive)
                    {
                        // slave transaction is not regarded as "own"; the scope does not control it.
                        scopeToWrap = fsProvider.CreateSlaveTransactionScope(dispose: disposeIfSlave);
                    }
                    else
                    {
                        if (okToStartOwnTransaction)
                        {
                            // default
                            // Creates new scope, starts new independent storage transaction regardless of current storage and managed context.
                            scopeToWrap = fsProvider.CreateStandaloneTransactionScope(dispose: true);
                        }
                        else
                        {
                            // NULL scope needed - the scope which will not create any transaction and will clear storage transaction context
                            // instead; this may happen when when there's storage ambient transaction but "always start new" option is ON.
                            scopeToWrap = fsProvider.CreateTransactionScope(transactionToUse: null, dispose: false);
                            Check.Assert(scopeToWrap.UnderlyingTransaction == null);
                            Check.Assert(!fsProvider.IsStorageAmbientTransactionActive);
                        }
                    }
                }
            }
            else
            {
                // creating scope which will clear transactional context
                scopeToWrap = fsProvider.CreateTransactionScope(transactionToUse: null, dispose: false);
            }

            Check.Assert(scopeToWrap != null);
            Initialize(scopeToWrap, repository);
        }