/// <summary> /// Sets the value for a parameter in a query. /// </summary> /// <param name="parameterName">Name of the parameter</param> /// <param name="value">Value of the parameter.</param> /// <exception cref="ArgumentNullException"><paramref name="parameterName"/> is a null reference.</exception> /// <exception cref="InvalidOperationException">Invalid /// <paramref name="value"/> value.</exception> /// <remarks> /// See the <Typ>LearningStoreQuery</Typ> documentation for more /// information about creating and executing queries. /// <p/> /// <paramref name="value"/> must contain a value /// that is valid based on the type of the parameter specified by /// <paramref name="parameterName"/>: /// <ul> /// <li><b>Boolean:</b> A System.Boolean, or an object that can be converted into /// a System.Boolean using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>ByteArray</b> A System.Byte array.</li> /// <li><b>DateTime:</b> A System.DateTime, or an object that can be converted into /// a System.DateTime using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Double:</b> A System.Double, or an object that can be converted into /// a System.Double using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Enumeration:</b> An System.Int32, or an object that can be converted into /// a System.Int32 using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Guid</b> A System.Guid or a string representing a Guid.</li> /// <li><b>Item identifier:</b> A <Typ>LearningStoreItemIdentifier</Typ> /// of the associated item type.</li> /// <li><b>Int32:</b> A System.Int32, or an object that can be converted into /// a System.Int32 using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Single:</b> A System.Single, or an object that can be converted into /// a System.Single using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>String:</b> A System.String, or an object that can be converted into /// a System.String using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Xml:</b> A <Typ>LearningStoreXml</Typ>.</li> /// </ul> /// <p/> /// <paramref name="value"/> must not contain a <Typ>LearningStoreItemIdentifier</Typ> returned /// from <Mth>../LearningStoreJob.AddItem</Mth>. /// </remarks> public void SetParameter(string parameterName, object value) { // Check input parameters if (parameterName == null) { throw new ArgumentNullException("parameterName"); } // Find the parameter in the view LearningStoreViewParameter parameter = null; if (!m_view.TryGetParameterByName(parameterName, out parameter)) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, LearningStoreStrings.ParameterNotFound, parameterName)); } // Cast the comparison value to the associated property type value = parameter.CastValue(value, m_locale, delegate(string reason, Exception innerException) { return(new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, LearningStoreStrings.InvalidParameterValueWithDescription, reason), innerException)); } ); // Verify special rules for LearningStoreItemIdentifier properties LearningStoreItemIdentifier idValue = value as LearningStoreItemIdentifier; if ((idValue != null) && (!idValue.HasKey)) { throw new InvalidOperationException( LearningStoreStrings.InvalidIdParameterValue); } // Add the value int existingParameterValueIndex = m_parameterValues.FindIndex(delegate(LearningStoreQueryParameterValue existingParameterValue) { return(Object.ReferenceEquals(existingParameterValue.Parameter, parameter)); }); if (existingParameterValueIndex == -1) { m_parameterValues.Add(new LearningStoreQueryParameterValue(parameter, value)); } else { m_parameterValues[existingParameterValueIndex] = new LearningStoreQueryParameterValue(parameter, value); } }
/// <summary> /// Create a LearningStoreItemIdentifier from a result within a LogableSqlCommand /// </summary> /// <param name="command">The LogableSqlCommand. On exit, the entire /// current result has been read.</param> /// <param name="itemType">Information about the item type that should be read.</param> /// <returns>The<Typ>LearningStoreItemIdentifier</Typ></returns> /// <remarks>Assumes that the result has one rows. Assumes that the /// columns within the row contain exactly the correct data /// for the <Mth>ReadItemIdentifierColumns</Mth> method.</remarks> public static LearningStoreItemIdentifier ReadItemIdentifierResult(LogableSqlCommand command, LearningStoreItemType itemType) { // Check input parameters if (command == null) { throw new LearningComponentsInternalException("LSTR1000"); } if (itemType == null) { throw new LearningComponentsInternalException("LSTR1010"); } if (!command.Read()) { throw new LearningComponentsInternalException("LSTR1020"); } // Start at the first column int startingColumn = 0; // Read the item LearningStoreItemIdentifier id = ReadItemIdentifierColumns(command, ref startingColumn, itemType); // Verify that the correct number of values are in the row if (command.GetFieldCount() != startingColumn) { throw new LearningComponentsInternalException("LSTR1030"); } if (command.Read()) { throw new LearningComponentsInternalException("LSTR1040"); } return(id); }
/// <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> /// Adds all the activities to the navigator, and performs initial setup. /// </summary> /// <param name="d">DataTable containing the activities to add.</param> /// <param name="rootActivityId">Root activity identifier.</param> /// <param name="learnerName">Name of the learner.</param> /// <param name="learnerLanguage">Preferred language used by the learner.</param> /// <param name="learnerCaption">Preferred captioning setting used by the learner.</param> /// <param name="learnerAudioLevel">Preferred audio level setting for the learner.</param> /// <param name="learnerDeliverySpeed">Preferred delivery speed for the learner.</param> /// <remarks> /// This is only called by ExecuteNavigator(). Removed from the middle of that function to get /// FxCop to shut up. /// </remarks> private void AddActivities(DataTable d, long rootActivityId, string learnerName, string learnerLanguage, AudioCaptioning learnerCaption, float learnerAudioLevel, float learnerDeliverySpeed) { // create activity objects and store them in a big dictionary foreach (DataRow row in d.Rows) { Activity act = new Activity(this, ((LearningStoreItemIdentifier)row[Schema.SeqNavActivityTreeView.Id]).GetKey(), ConvertLearningStoreXmlToXPathNavigator(row[Schema.SeqNavActivityTreeView.DataModelCache] as LearningStoreXml, true), null, null, null, WrapAttachment, WrapAttachmentGuid, -1, (bool)row[Schema.SeqNavActivityTreeView.ObjectivesGlobalToSystem], DataModelWriteValidationMode.AllowWriteOnlyIfActive, m_learnerId.ToString(System.Globalization.CultureInfo.InvariantCulture), learnerName, learnerLanguage, learnerCaption, learnerAudioLevel, learnerDeliverySpeed); m_activities[act.ActivityId] = act; if (act.ActivityId == rootActivityId) { 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(); m_activities[id].Parent = m_activities[parentId.GetKey()]; m_activities[parentId.GetKey()].AddChild(m_activities[id]); } } // make sure children of each parent are in the right order, and set Previous and Next pointer correctly. 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. #if DEBUG Random rand; if (NavigatorData.Random != null) { rand = NavigatorData.Random; } else { rand = new Random(); } #else Random rand = new Random(); #endif foreach (Activity a in 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; while (a.Children.Count > selCount) { a.RemoveChild(rand.Next(a.Children.Count)); } 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(); } } } }
/// <summary> /// Add a condition that restricts the data returned in a query. /// </summary> /// <param name="columnName">Name of the column to be compared</param> /// <param name="conditionOperator">Condition operator</param> /// <param name="conditionValue">Value to be compared against.</param> /// <exception cref="ArgumentNullException"><paramref name="columnName"/> is a null reference.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="conditionOperator"/> is invalid.</exception> /// <exception cref="InvalidOperationException"> /// Invalid comparison, the column name wasn't found in the view, or an invalid /// <paramref name="conditionValue"/> value.</exception> /// <remarks> /// See the <Typ>LearningStoreQuery</Typ> documentation for more /// information about creating and executing queries. /// <p/> /// <paramref name="conditionValue"/> must contain a value /// that is valid based on the type of the column specified by /// <paramref name="columnName"/>: /// <ul> /// <li><b>Boolean:</b> A System.Boolean, or an object that can be converted into /// a System.Boolean using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>ByteArray</b> A System.Byte array.</li> /// <li><b>DateTime:</b> A System.DateTime, or an object that can be converted into /// a System.DateTime using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Double:</b> A System.Double, or an object that can be converted into /// a System.Double using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Enumeration:</b> An System.Int32, or an object that can be converted into /// a System.Int32 using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Guid</b> A System.Guid or a string representing a Guid.</li> /// <li><b>Item identifier:</b> A <Typ>LearningStoreItemIdentifier</Typ> /// of the associated item type.</li> /// <li><b>Int32:</b> A System.Int32, or an object that can be converted into /// a System.Int32 using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Single:</b> A System.Single, or an object that can be converted into /// a System.Single using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>String:</b> A System.String, or an object that can be converted into /// a System.String using <Typ>/System.IConvertible</Typ>.</li> /// <li><b>Xml:</b> A <Typ>LearningStoreXml</Typ>.</li> /// </ul> /// <p/> /// <paramref name="conditionValue"/> must not contain a <Typ>LearningStoreItemIdentifier</Typ> returned /// from <Mth>../LearningStoreJob.AddItem</Mth>. /// <p/> /// If <paramref name="columnName"/> refers to an XML column, then the following restrictions apply: /// <ul> /// <li><paramref name="conditionOperator"/> must be <Fld>../LearningStoreConditionOperator.Equal</Fld> or /// <Fld>../LearningStoreConditionOperator.NotEqual</Fld></li> /// <li><paramref name="conditionValue"/> must be null.</li> /// </ul> /// <p/> /// If <paramref name="columnName"/> refers to an Guid column, then the following restrictions apply: /// <ul> /// <li><paramref name="conditionOperator"/> must be <Fld>../LearningStoreConditionOperator.Equal</Fld> or /// <Fld>../LearningStoreConditionOperator.NotEqual</Fld></li> /// </ul> /// <p/> /// If <paramref name="conditionOperator"/> is <Fld>../LearningStoreConditionOperator.GreaterThan</Fld>, /// <Fld>../LearningStoreConditionOperator.GreaterThanEqual</Fld>, /// <Fld>../LearningStoreConditionOperator.LessThan</Fld>, or /// <Fld>../LearningStoreConditionOperator.LessThanEqual</Fld>, then /// <paramref name="conditionValue"/> must not contain null. /// <p/> /// Care must be taken when null is involved in a comparison. In /// particular:<ul> /// <li>null == null: True</li> /// <li>null != null: False</li> /// <li>null == any other value: False</li> /// <li>null != any other value: True</li> /// <li>null < any other value: False</li> /// <li>null > any other value: False</li> /// </ul> /// </remarks> /// <example> /// The following code creates a new query that returns the id and name /// of the users with a name less than "Joe": /// <code language="C#"> /// LearningStoreQuery query = store.CreateQuery("UserItem"); /// query.AddColumn("Id"); /// query.AddColumn("Name"); /// query.AddCondition("Name", LearningStoreConditionOperator.LessThan, "Joe"); /// </code> /// </example> public void AddCondition(string columnName, LearningStoreConditionOperator conditionOperator, object conditionValue) { // Check input parameters if (columnName == null) { throw new ArgumentNullException("columnName"); } else if ((conditionOperator != LearningStoreConditionOperator.Equal) && (conditionOperator != LearningStoreConditionOperator.GreaterThan) && (conditionOperator != LearningStoreConditionOperator.GreaterThanEqual) && (conditionOperator != LearningStoreConditionOperator.LessThan) && (conditionOperator != LearningStoreConditionOperator.LessThanEqual) && (conditionOperator != LearningStoreConditionOperator.NotEqual)) { throw new ArgumentOutOfRangeException("conditionOperator"); } // Find the column in the view LearningStoreViewColumn column = null; if (!m_view.TryGetColumnByName(columnName, out column)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, LearningStoreStrings.ColumnNotFound, columnName)); } // Cast the comparison value to the associated property type conditionValue = column.CastValue(conditionValue, m_locale, delegate(string reason, Exception innerException) { return(new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, LearningStoreStrings.InvalidConditionValueWithDescription, reason), innerException)); } ); // Fail if operator isn't compatible with null if ((conditionValue == null) && (conditionOperator != LearningStoreConditionOperator.Equal) && (conditionOperator != LearningStoreConditionOperator.NotEqual)) { throw new InvalidOperationException(LearningStoreStrings.InvalidOperatorForNullConditionValue); } // Verify special rules for Xml properties if ((column.IsXml) && (conditionValue != null)) { throw new InvalidOperationException(LearningStoreStrings.InvalidConditionValueForXmlColumn); } // Verify special rules for Guid properties if ((column.IsGuid) && (conditionOperator != LearningStoreConditionOperator.Equal) && (conditionOperator != LearningStoreConditionOperator.NotEqual)) { throw new InvalidOperationException(LearningStoreStrings.InvalidConditionOperatorForGuidColumn); } // Verify special rules for LearningStoreItemIdentifier properties LearningStoreItemIdentifier idConditionValue = conditionValue as LearningStoreItemIdentifier; if ((idConditionValue != null) && (!idConditionValue.HasKey)) { throw new InvalidOperationException(LearningStoreStrings.InvalidIdConditionValue); } // Add the condition LearningStoreCondition condition = new LearningStoreCondition(column, conditionOperator, conditionValue); m_conditions.Add(condition); }