/// <summary>
        /// Get the schema information for this store.
        /// </summary>
        /// <returns>The schema information</returns>
        /// <remarks>
        /// If it has already been retrieved for this store, then just
        /// return that copy.  If it hasn't been retrieved, then grab
        /// it from the AppDomain-wide cache or from the database.
        /// </remarks>
        internal LearningStoreSchema GetSchema()
        {
            // If the information isn't available, then call the static
            // method to cache schema information
            if (m_schema == null)
            {
                m_schema = GetSchemaInformationFromCache(m_connectionString, m_impersonationBehavior, m_debugLog);
            }

            // Return it
            return(m_schema);
        }
        /// <summary>
        /// Create a new instance of the <Typ>LearningStoreQuery</Typ> class
        /// </summary>
        /// <returns>A new <Typ>LearningStoreQuery</Typ></returns>
        /// <param name="viewName">Name of the view for the query.  If this is an item type name,
        ///     then the default view for the item type is used.</param>
        /// <exception cref="ArgumentNullException"><paramref name="viewName"/> is a null reference.</exception>
        /// <exception cref="InvalidOperationException">The store was created
        ///     by a different version of this product, the schema information stored
        ///     inside the database is invalid, or <paramref name="viewName"/>
        ///     does not refer to a view or item type in the store.</exception>
        /// <exception cref="SqlException">The database could not be
        ///     opened or other SQL error.</exception>
        /// <remarks>
        /// See the <Typ>LearningStoreQuery</Typ> documentation for more information
        /// about constructing queries.
        /// </remarks>
        /// <example>The following code opens a store and creates a new query:
        /// <code language="C#">
        /// LearningStore store = new LearningStore("Server=Learning;Database=Mls;Integrated Security=true", "Bob");
        /// LearningStoreQuery query = store.CreateQuery("AttemptItem");
        /// </code>
        /// </example>
        public LearningStoreQuery CreateQuery(string viewName)
        {
            // Check parameters
            if (viewName == null)
            {
                throw new ArgumentNullException("viewName");
            }

            // Get the information about the item
            LearningStoreSchema schema = GetSchema();
            LearningStoreView   view   = null;

            if (!schema.TryGetViewByName(viewName, out view))
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
                                                                  LearningStoreStrings.ViewNotFoundInSchema, viewName));
            }

            return(new LearningStoreQuery(this, view, m_locale));
        }
        /// <summary>
        /// Get the schema information for this store from the cache or from the database.
        /// </summary>
        /// <param name="connectionString">Connection string used to access the store.</param>
        /// <param name="impersonationBehavior">Identifies which <c>WindowsIdentity</c> is used to
        ///     access the database when impersonation is involved.</param>
        /// <param name="debugLog">Location to which the debug log should be written, or
        ///     null if there isn't a debug log.</param>
        /// <returns>The schema information.</returns>
        private static LearningStoreSchema GetSchemaInformationFromCache(string connectionString,
                                                                         ImpersonationBehavior impersonationBehavior, TextWriter debugLog)
        {
            // Try to find the connection in the cache
            lock (s_allSchemasLock)
            {
                LearningStoreSchema schema;
                if (s_allSchemas.TryGetValue(connectionString, out schema))
                {
                    return(schema);
                }
            }

            // Not found in the cache -- so go get it from the database

            // This try/catch block is here for security reasons. Search MSDN for
            // "WrapVulnerableFinallyClausesInOuterTry" to see details.
            try
            {
                WindowsImpersonationContext impersonationContext = null;

                try
                {
                    // Impersonate if necessary
                    if (impersonationBehavior == ImpersonationBehavior.UseOriginalIdentity)
                    {
                        // Not adding it to the disposer, since that could fail (e.g., OutOfMemoryException),
                        // which could cause a security hole.  Instead, we'll clean it up manually later.
                        impersonationContext = WindowsIdentity.Impersonate(IntPtr.Zero);
                    }

                    using (Microsoft.LearningComponents.Disposer disposer = new Microsoft.LearningComponents.Disposer())
                    {
                        // Create a connection
                        SqlConnection connection = new SqlConnection(connectionString);
                        disposer.Push(connection);
                        connection.Open();

                        // Create a command to retrieve information from the configuration table
                        LogableSqlCommand command = new LogableSqlCommand(connection, debugLog);
                        disposer.Push(command);

                        // Execute
                        command.Execute(
                            "SELECT EngineVersion,\r\n" +
                            "    SchemaDefinition\r\n" +
                            "FROM Configuration\r\n");

                        // Read return values from the database
                        if (!command.Read())
                        {
                            throw new LearningComponentsInternalException("LSTR1500");
                        }
                        if (command.GetFieldCount() != 2)
                        {
                            throw new LearningComponentsInternalException("LSTR1510");
                        }
                        int           engineVersion = command.GetInt32(0);
                        SqlXml        schemaXml     = command.GetSqlXml(1);
                        XPathDocument schemaDoc;
                        using (XmlReader reader = schemaXml.CreateReader())
                        {
                            schemaDoc = new XPathDocument(reader);
                        }
                        LearningStoreSchema newSchema = LearningStoreSchema.CreateSchema(schemaDoc);
                        if (command.Read())
                        {
                            throw new LearningComponentsInternalException("LSTR1520");
                        }
                        if (command.NextResult())
                        {
                            throw new LearningComponentsInternalException("LSTR1530");
                        }

                        // Fail if a different engine created this
                        if (engineVersion != LearningStore.EngineVersion)
                        {
                            throw new InvalidOperationException(LearningStoreStrings.IncompatibleEngineVersion);
                        }

                        // Save it in the cache
                        lock (s_allSchemasLock)
                        {
                            LearningStoreSchema schema;
                            if (s_allSchemas.TryGetValue(connectionString, out schema))
                            {
                                return(schema);
                            }
                            s_allSchemas.Add(connectionString, newSchema);
                        }

                        return(newSchema);
                    }
                }
                finally
                {
                    if (impersonationContext != null)
                    {
                        impersonationContext.Dispose();
                    }
                }
            }
            catch
            {
                throw;
            }
        }