/// <summary>
        /// Initializes a new instance of the <Typ>LearningStoreTransactionScope</Typ> class.  LearningStore
        /// jobs executed within the scope use the specified transaction.
        /// </summary>
        /// <param name="transaction">Transaction used by LearningStore operations within the scope.</param>
        /// <exception cref="ArgumentNullException"><paramref name="transaction"/> is a null reference.</exception>
        /// <exception cref="InvalidOperationException">A transaction already exists in this scope (i.e.,
        ///     this scope is nested inside another).</exception>
        /// <remarks>
        /// See <Typ>LearningStoreTransactionScope</Typ> for more information.
        /// </remarks>
        public LearningStoreTransactionScope(Transaction transaction)
        {
            // Check parameters
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }

            // Fail if there's already a scope
            if (s_currentScope != null)
            {
                throw new InvalidOperationException(LearningStoreStrings.TransactionAlreadyExists);
            }

            m_scopeThread = Thread.CurrentThread;

            // Use the transaction passed to us
            m_dependentTransaction = transaction.DependentClone(DependentCloneOption.RollbackIfNotComplete);
            m_transaction          = transaction;
            m_createdConnections   = new Dictionary <string, SqlConnection>();
            m_connections          = m_createdConnections;

            // Enter the scope
            s_currentScope = this;
        }
Example #2
0
        public AddPackageResult AddPackage(PackageReader packageReader, PackageEnforcement packageEnforcement)
        {
            Utilities.ValidateParameterNonNull("packageReader", packageReader);
            Utilities.ValidateParameterNonNull("packageEnforcement", packageEnforcement);

            AddPackageReferenceResult refResult;
            string packageLocation = null;

            try
            {
                TransactionOptions options = new TransactionOptions();
                options.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead;
                using (LearningStoreTransactionScope scope = new LearningStoreTransactionScope(options))
                {
                    // Add the reference to the package
                    refResult = AddPackageReference(packageReader, "$Invalid Value$", packageEnforcement);

                    // Add the resource files and determine the location of them. The location is derived from the packageId.
                    packageLocation = ImportFiles(refResult.PackageId, packageReader);

                    // Update the package location in LearningStore
                    UpdatePackageLocation(refResult.PackageId, packageLocation);

                    scope.Complete();
                }
            }
            catch (Exception)
            {
                try
                {
                    // Delete files if necessary
                    if (!String.IsNullOrEmpty(packageLocation))
                    {
                        DeleteFiles(packageLocation);
                    }
                }
                catch
                {
                    // If the deletion failed, make sure the original exception is thrown to the caller
                }
                throw;
            }
            return(new AddPackageResult(refResult));
        }
        /// <summary>
        /// Initializes a new instance of the <Typ>LearningStoreTransactionScope</Typ> class with
        /// the specified options.
        /// </summary>
        /// <param name="transactionOptions">The transaction options to use if a new transaction is created (i.e.,
        ///     if this is the outermost scope).  If a new transaction is not created (i.e., this scope is nested
        ///     inside another), then the isolation level must either be unspecified or identical to the
        ///     currently-existing transaction, and the timeout value must be zero.
        ///     </param>
        /// <exception cref="System.InvalidOperationException">The transaction options specified are invalid.</exception>
        /// <remarks>
        /// See <Typ>LearningStoreTransactionScope</Typ> for more information.
        /// </remarks>
        public LearningStoreTransactionScope(TransactionOptions transactionOptions)
        {
            m_priorScope  = s_currentScope;
            m_scopeThread = Thread.CurrentThread;

            if (m_priorScope == null)
            {
                // Create new transaction

                m_committableTransaction = new CommittableTransaction(transactionOptions);
                m_transaction            = m_committableTransaction;
                m_createdConnections     = new Dictionary <string, SqlConnection>();
                m_connections            = m_createdConnections;
            }
            else
            {
                // Transaction already exists

                // Verify that the passed-in options are compatible
                if ((transactionOptions.IsolationLevel != System.Transactions.IsolationLevel.Unspecified) &&
                    (transactionOptions.IsolationLevel != m_priorScope.Transaction.IsolationLevel))
                {
                    throw new InvalidOperationException(LearningStoreStrings.MismatchedIsolationLevel);
                }
                if (transactionOptions.Timeout != TimeSpan.Zero)
                {
                    throw new InvalidOperationException(LearningStoreStrings.TimeoutMustBeZero);
                }

                m_dependentTransaction = m_priorScope.m_transaction.DependentClone(DependentCloneOption.RollbackIfNotComplete);
                m_transaction          = m_priorScope.m_transaction;
                m_connections          = m_priorScope.m_connections;
            }

            // Enter the scope
            if (m_priorScope != null)
            {
                m_priorScope.m_nextScope = this;
            }
            s_currentScope = this;
        }
Example #4
0
        /// <summary>
        /// Creates a new <Typ>ExecuteNavigator</Typ> object in memory and its representation in the database.
        /// </summary>
        /// <param name="store">A <Typ>LearningStore</Typ> object that references the database to use.</param>
        /// <param name="rootActivityId">The database identifier for the root activity (i.e. organization) of the activity tree to attempt.</param>
        /// <param name="learnerId">The database identifier for the learner information.</param>
        /// <param name="learnerAssignmentId">The database identifier for the learner assignment information. Only used in SLK.</param>
        /// <param name="loggingFlags">Flags specifying which actions to log.</param>
        /// <returns>A new <Typ>ExecuteNavigator</Typ> object.</returns>
        /// <exception cref="LearningStoreItemNotFoundException">Thrown if the learner ID or root activity ID is invalid, or if the package information cannot be found.</exception>
        static public ExecuteNavigator CreateExecuteNavigator(LearningStore store, long rootActivityId, long learnerId, long learnerAssignmentId, LoggingOptions loggingFlags)
        {
            ExecuteNavigator eNav = new ExecuteNavigator();

            LearningStoreJob job = store.CreateJob();

            // first add security
            Dictionary <string, object> securityParameters = new Dictionary <string, object>();

            securityParameters[Schema.CreateAttemptRight.RootActivityId] =
                new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, rootActivityId);
            securityParameters[Schema.CreateAttemptRight.LearnerId] =
                new LearningStoreItemIdentifier(Schema.UserItem.ItemTypeName, learnerId);
            job.DemandRight(Schema.CreateAttemptRight.RightName, securityParameters);
            job.DisableFollowingSecurityChecks();

            // first query for all information about the learner
            LearningStoreQuery query = store.CreateQuery(Schema.UserItem.ItemTypeName);

            query.AddColumn(Schema.UserItem.Id);
            query.AddColumn(Schema.UserItem.Name);
            query.AddColumn(Schema.UserItem.Language);
            query.AddColumn(Schema.UserItem.AudioCaptioning);
            query.AddColumn(Schema.UserItem.AudioLevel);
            query.AddColumn(Schema.UserItem.DeliverySpeed);
            query.AddCondition(Schema.UserItem.Id, LearningStoreConditionOperator.Equal,
                               new LearningStoreItemIdentifier(Schema.UserItem.ItemTypeName, learnerId));
            job.PerformQuery(query);

            // then query the package information
            query = store.CreateQuery(Schema.SeqNavActivityPackageView.ViewName);
            LearningStoreItemIdentifier rootId = new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, rootActivityId);

            query.AddColumn(Schema.SeqNavActivityPackageView.PackageId);
            query.AddColumn(Schema.SeqNavActivityPackageView.PackageFormat);
            query.AddColumn(Schema.SeqNavActivityPackageView.PackagePath);
            query.AddCondition(Schema.SeqNavActivityPackageView.Id, LearningStoreConditionOperator.Equal, rootId);
            job.PerformQuery(query);

            // then query for the activity tree
            query = store.CreateQuery(Schema.SeqNavActivityTreeView.ViewName);
            query.AddColumn(Schema.SeqNavActivityTreeView.DataModelCache);
            query.AddColumn(Schema.SeqNavActivityTreeView.ParentActivityId);
            query.AddColumn(Schema.SeqNavActivityTreeView.Id);
            query.AddColumn(Schema.SeqNavActivityTreeView.ObjectivesGlobalToSystem);
            query.AddCondition(Schema.SeqNavActivityTreeView.RootActivityId, LearningStoreConditionOperator.Equal, new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, rootActivityId));
            job.PerformQuery(query);

            DataTable d;  // used to store results of query
            ReadOnlyCollection <object> ids;

            // for this transaction we need to read from the activitypackageitem table and write new records to
            // the activityattemptitem and attemptitem tables
            TransactionOptions options = new TransactionOptions();

            options.IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead;
            using (LearningStoreTransactionScope scope = new LearningStoreTransactionScope(options))
            {
                // execute the query
                ReadOnlyCollection <object> c = job.Execute();
                Utilities.Assert(c.Count == 3);

                if (((DataTable)c[0]).Rows.Count < 1)
                {
                    throw new LearningStoreItemNotFoundException(Resources.InvalidLearnerId);
                }
                if (((DataTable)c[1]).Rows.Count < 1)
                {
                    throw new LearningStoreItemNotFoundException(Resources.InvalidRootActivityId);
                }
                if (((DataTable)c[2]).Rows.Count < 1)
                {
                    throw new LearningStoreItemNotFoundException(Resources.InvalidRootActivityId);
                }

                d = (DataTable)c[0];  // save learner information

                string          learnerName          = (string)d.Rows[0][Schema.UserItem.Name];
                AudioCaptioning learnerCaption       = (AudioCaptioning)d.Rows[0][Schema.UserItem.AudioCaptioning];
                float           learnerAudioLevel    = (float)d.Rows[0][Schema.UserItem.AudioLevel];
                float           learnerDeliverySpeed = (float)d.Rows[0][Schema.UserItem.DeliverySpeed];
                string          learnerLanguage      = (string)d.Rows[0][Schema.UserItem.Language];

                d = (DataTable)c[1];  // save package information

                // we need to create the activity tree within the transaction because it may affect the data written
                // by means of the selection and randomization processes.

                eNav.m_packageFormat   = (PackageFormat)d.Rows[0][Schema.SeqNavActivityPackageView.PackageFormat];
                eNav.m_packageId       = ((LearningStoreItemIdentifier)d.Rows[0][Schema.SeqNavActivityPackageView.PackageId]).GetKey();
                eNav.m_packageLocation = (string)d.Rows[0][Schema.SeqNavActivityPackageView.PackagePath];
                eNav.m_store           = store;
                eNav.m_learnerId       = learnerId;
                eNav.m_loggingFlags    = loggingFlags;

                // we must set this here so that the Activity  constructor doesn't try and load missing info
                eNav.m_attemptItemInformationIsValid = true;

                d = (DataTable)c[2];  // save data to create activity tree later, when we are done with sql processing

                eNav.AddActivities(d, rootActivityId, learnerName, learnerLanguage, learnerCaption, learnerAudioLevel, learnerDeliverySpeed);

                /*
                 * // create activity objects and store them in a big dictionary
                 * foreach(DataRow row in d.Rows)
                 * {
                 *  Activity act = new Activity(eNav, ((LearningStoreItemIdentifier)row[Schema.SeqNavActivityTreeView.Id]).GetKey(),
                 *      ConvertLearningStoreXmlToXPathNavigator(row[Schema.SeqNavActivityTreeView.DataModelCache] as LearningStoreXml, true),
                 *      null, null, null, eNav.WrapAttachment, eNav.WrapAttachmentGuid, -1,
                 *      (bool)row[Schema.SeqNavActivityTreeView.ObjectivesGlobalToSystem], eNav.m_learnerId.ToString(System.Globalization.CultureInfo.InvariantCulture),
                 *      learnerName, learnerLanguage, learnerCaption, learnerAudioLevel, learnerDeliverySpeed);
                 *  eNav.m_activities[act.ActivityId] = act;
                 *  if(act.ActivityId == rootActivityId)
                 *  {
                 *      eNav.RootActivity = act;
                 *      if(!(row[Schema.SeqNavActivityTreeView.ParentActivityId] is DBNull))
                 *      {
                 *          throw new LearningStoreItemNotFoundException(Resources.InvalidRootActivityId);
                 *      }
                 *  }
                 * }
                 *
                 * // now that we have all the activities in a big dictionary, find all the parents to build the tree
                 * // in theory this could be done in the first loop if I sort the query by parentid, but that's making a lot
                 * // of assumptions about the structure of the database that I don't think are safe to make
                 * foreach(DataRow row in d.Rows)
                 * {
                 *  LearningStoreItemIdentifier parentId = row[Schema.SeqNavActivityTreeView.ParentActivityId] as LearningStoreItemIdentifier;
                 *  if (parentId != null)
                 *  {
                 *      long id = ((LearningStoreItemIdentifier)row[Schema.SeqNavActivityTreeView.Id]).GetKey();
                 *      eNav.m_activities[id].Parent = eNav.m_activities[parentId.GetKey()];
                 *      eNav.m_activities[parentId.GetKey()].AddChild(eNav.m_activities[id]);
                 *  }
                 * }
                 *
                 * // make sure children of each parent are in the right order, and set Previous and Next pointer correctly.
                 * eNav.SortActivityTree();
                 *
                 * // step through the activity tree searching for selection and randomization instructions
                 * // this must be done before the ActivityAttemptItems are saved because they may reorder
                 * // and potentially even remove children.
                 * Random rand = new Random();
                 * foreach(Activity a in eNav.Traverse)
                 * {
                 *  if(!a.IsLeaf)
                 *  {
                 *      // if there's a valid selection instruction, remove excess
                 *      // child activities randomly
                 *      if(a.Sequencing.SelectionTiming == RandomizationTiming.Once &&
                 *          a.Sequencing.RandomizationSelectCount > 0 &&
                 *          a.Sequencing.RandomizationSelectCount < a.Children.Count)
                 *      {
                 *          int selCount = a.Sequencing.RandomizationSelectCount;
                 *          for(int i = 0 ; i < a.Children.Count ; ++i)
                 *          {
                 *              if(rand.Next(a.Children.Count) < selCount)
                 *              {
                 *                  --selCount;
                 *              }
                 *              else
                 *              {
                 *                  a.RemoveChild(i);
                 *              }
                 *          }
                 *          a.SortChildren();
                 *      }
                 *
                 *      // if there's a valid randomization instruction, randomize order of the children
                 *      // of this activity
                 *      if((a.Sequencing.RandomizationTiming == RandomizationTiming.Once ||
                 *          a.Sequencing.RandomizationTiming == RandomizationTiming.OnEachNewAttempt) &&
                 *          a.Sequencing.ReorderChildren)
                 *      {
                 *          List<Activity> randlist = new List<Activity>();
                 *          while(a.Children.Count > 0)
                 *          {
                 *              int i = rand.Next(a.Children.Count);
                 *              randlist.Add((Activity)a.Children[i]);
                 *              a.RemoveChild(i);
                 *          }
                 *          for(int i = 0 ; i < randlist.Count ; ++i)
                 *          {
                 *              randlist[i].RandomPlacement = i;
                 *              a.AddChild(randlist[i]);
                 *          }
                 *          a.SortChildren();
                 *      }
                 *  }
                 * }
                 * */

                // create the attemptitem
                // fill in fields with no defaults
                job = store.CreateJob();
                job.DisableFollowingSecurityChecks();
                Dictionary <string, object> attempt = new Dictionary <string, object>();
                attempt[Schema.AttemptItem.RootActivityId] = new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, rootActivityId);
                attempt[Schema.AttemptItem.LearnerId]      = new LearningStoreItemIdentifier(Schema.UserItem.ItemTypeName, learnerId);
                if (learnerAssignmentId != 0)
                {
                    attempt[Schema.AttemptItem.LearnerAssignmentId] = new LearningStoreItemIdentifier("LearnerAssignmentItem", learnerAssignmentId);
                }
                attempt[Schema.AttemptItem.PackageId] = new LearningStoreItemIdentifier(Schema.PackageItem.ItemTypeName, eNav.m_packageId);
                eNav.m_attemptStartTime = DateTime.UtcNow;
                attempt[Schema.AttemptItem.StartedTimestamp]    = eNav.m_attemptStartTime;
                attempt[Schema.AttemptItem.LogDetailSequencing] = ((loggingFlags & LoggingOptions.LogDetailSequencing) == LoggingOptions.LogDetailSequencing);
                attempt[Schema.AttemptItem.LogFinalSequencing]  = ((loggingFlags & LoggingOptions.LogFinalSequencing) == LoggingOptions.LogFinalSequencing);
                attempt[Schema.AttemptItem.LogRollup]           = ((loggingFlags & LoggingOptions.LogRollup) == LoggingOptions.LogRollup);
                LearningStoreItemIdentifier attemptId = job.AddItem(Schema.AttemptItem.ItemTypeName, attempt, true);

                // create activityattemptitems for each activity in the tree
                foreach (Activity a in eNav.Traverse)
                {
                    Dictionary <string, object> activity = new Dictionary <string, object>();

                    // fill in everything that has no default
                    activity[Schema.ActivityAttemptItem.AttemptId]         = attemptId;
                    activity[Schema.ActivityAttemptItem.ActivityPackageId] = new LearningStoreItemIdentifier(Schema.ActivityPackageItem.ItemTypeName, a.ActivityId);
                    if (a.RandomPlacement >= 0)
                    {
                        activity[Schema.ActivityAttemptItem.RandomPlacement] = a.RandomPlacement;
                        // sibling activities are ordered by RandomPlacement first,
                        // then by OriginalPlacment; RandomPlacement is -1 for all siblings if not randomization occurs
                    }
                    job.AddItem(Schema.ActivityAttemptItem.ItemTypeName, activity, true);
                }
                ids = job.Execute();
                scope.Complete();
            }

            // fill in some vital data for the navigator
            eNav.m_attemptId = ((LearningStoreItemIdentifier)ids[0]).GetKey();
            int index = 1;

            foreach (Activity a in eNav.Traverse)
            {
                a.InternalActivityId = ((LearningStoreItemIdentifier)ids[index++]).GetKey();
            }

            return(eNav);
        }
        /// <summary>
        /// Ends the transaction scope.
        /// </summary>
        /// <exception cref="InvalidOperationException">A thread other than the creating thread
        ///     is disposing the object.</exception>
        /// <remarks>
        /// See <Typ>LearningStoreTransactionScope</Typ> for more information.
        /// </remarks>
        public void Dispose()
        {
            // Skip if already disposed
            if (m_disposed)
            {
                return;
            }

            // Fail if this is being called on a different thread
            if (m_scopeThread != Thread.CurrentThread)
            {
                throw new InvalidOperationException(LearningStoreStrings.ScopeDisposedInInvalidThread);
            }

            if (m_nextScope != null)
            {
                // There are scopes "above" this one -- which means the user called
                // Dispose out-of-order.

                // First dispose the scope(s) above this one
                m_nextScope.Dispose();
                if (m_nextScope != null)
                {
                    throw new LearningComponentsInternalException("LSTR3100");
                }
                if (s_currentScope != this)
                {
                    throw new LearningComponentsInternalException("LSTR3110");
                }
            }

            // Leave the scope
            if (m_priorScope != null)
            {
                m_priorScope.m_nextScope = null;
            }
            s_currentScope = m_priorScope;

            // Mark as disposed
            m_disposed = true;

            // Dispose the connections if necessary
            if (m_createdConnections != null)
            {
                foreach (SqlConnection connection in m_createdConnections.Values)
                {
                    connection.Dispose();
                }
                m_createdConnections.Clear();
            }

            try
            {
                // Handle the dependent transaction if necessary
                if (m_dependentTransaction != null)
                {
                    if (m_complete)
                    {
                        m_dependentTransaction.Complete();
                    }
                    else
                    {
                        m_dependentTransaction.Rollback();
                    }
                }

                // Handle the committable transaction if necessary
                if (m_committableTransaction != null)
                {
                    if (m_complete)
                    {
                        m_committableTransaction.Commit();
                    }
                    else
                    {
                        m_committableTransaction.Rollback();
                    }
                }
            }
            finally
            {
                if (m_dependentTransaction != null)
                {
                    m_dependentTransaction.Dispose();
                }
                if (m_committableTransaction != null)
                {
                    m_committableTransaction.Dispose();
                }
            }
        }