/// <summary>
        /// Deserializes any tables defined in the specified <see cref="XElement"/> object into the specified <see cref="HyperDbSchema"/> object.
        /// </summary>
        /// <param name="parent">The <see cref="XElement"/> object containing the XML definitions for the tables.</param>
        /// <param name="dbSchema">The <see cref="HyperDbSchema"/> object to receive the deserialized tables.</param>
        private void DeserializeTables(XElement parent, HyperDbSchema dbSchema)
        {
            var defaultScriptWriterName = GetOptionalAttribute(parent, "scriptWriter", null);

            foreach (var tableElement in parent.Elements(_hyperDbXmlNamespace + "table"))
            {
                var table = new HyperDbTable
                {
                    Name = GetRequiredAttribute(tableElement, "name")
                };

                var     versionString = GetOptionalAttribute(tableElement, "version", null);
                Version versionObject;
                if (Version.TryParse(versionString, out versionObject))
                {
                    table.Version = versionObject;
                }

                var scriptWriterType = _defaultDbTableScriptWriterType;
                var scriptWriterName = GetOptionalAttribute(tableElement, "scriptWriter", defaultScriptWriterName);
                if (!string.IsNullOrWhiteSpace(scriptWriterName))
                {
                    if (_scriptWriterTypes.ContainsKey(scriptWriterName))
                    {
                        scriptWriterType = _scriptWriterTypes[scriptWriterName];
                    }
                    else
                    {
                        throw new HyperDbSchemaConfigurationException("Invalid " + typeof(IDbTableScriptWriter).Name + " was specified for table " + table.Name + ".");
                    }
                }

                var columnCollectionElement = tableElement.Element(_hyperDbXmlNamespace + "columns");
                if (columnCollectionElement != null)
                {
                    DeserializeColumns(columnCollectionElement, table);
                }

                var constraintCollectionElement = tableElement.Element(_hyperDbXmlNamespace + "constraints");
                if (constraintCollectionElement != null)
                {
                    DeserializeConstraints(constraintCollectionElement, table);
                }

                // Finally fill in our custom writer objects
                var scriptWriterObject = CreateInstanceAs <IDbTableScriptWriter>(scriptWriterType);
                scriptWriterObject.Source = table;
                table.ScriptWriter        = scriptWriterObject;

                dbSchema.Tables.Add(table);
            }
        }
        /// <summary>
        /// Deserializes any foreign keys defined in the specified <see cref="XElement"/> object into the specified <see cref="HyperDbSchema"/> object.
        /// </summary>
        /// <param name="parent">The <see cref="XElement"/> object containing the XML definitions for the foreign keys.</param>
        /// <param name="table">The <see cref="HyperDbTable"/> object to receive the deserialized foreign keys.</param>
        private void DeserializeForeignKeys(XElement parent, HyperDbTable table)
        {
            var defaultScriptWriterName = GetOptionalAttribute(parent, "scriptWriter", null);

            foreach (var foreignKeyElement in parent.Elements(_hyperDbXmlNamespace + "foreignKey"))
            {
                var foreignKey = new HyperDbForeignKey
                {
                    // Name attribute is required because multiple foreign keys with the same definition are allowed to exist in some (all?) databases.
                    // The only way to uniquely identify one is by name.
                    ForeignKeyName      = GetRequiredAttribute(foreignKeyElement, "name"),
                    ReferencedTableName = GetRequiredAttribute(foreignKeyElement, "referencedTable"),
                    TableSource         = table
                };

                var scriptWriterType = _defaultDbForeignKeyScriptWriterType;
                var scriptWriterName = GetOptionalAttribute(foreignKeyElement, "scriptWriter", defaultScriptWriterName);
                if (!string.IsNullOrWhiteSpace(scriptWriterName))
                {
                    if (_scriptWriterTypes.ContainsKey(scriptWriterName))
                    {
                        scriptWriterType = _scriptWriterTypes[scriptWriterName];
                    }
                    else
                    {
                        throw new HyperDbSchemaConfigurationException("Invalid " + typeof(IDbForeignKeyScriptWriter).Name + " was specified for table " + table.Name + ".");
                    }
                }

                // Per our XML schema, if we have a foreignKey element, we will always have at least one keyColumn element
                foreach (var keyColumnElement in foreignKeyElement.Elements(_hyperDbXmlNamespace + "keyColumn"))
                {
                    foreignKey.KeyColumns.Add(
                        new DbForeignKeyMapping
                    {
                        ForeignKeyColumnName = GetRequiredAttribute(keyColumnElement, "name"),
                        ReferencedColumnName = GetRequiredAttribute(keyColumnElement, "referencedColumn")
                    }
                        );
                }

                // Fill in our script writer
                var scriptWriterObject = CreateInstanceAs <IDbForeignKeyScriptWriter>(scriptWriterType);
                scriptWriterObject.Source = foreignKey;
                foreignKey.ScriptWriter   = scriptWriterObject;

                table.ForeignKeys.Add(foreignKey);
            }
        }
 /// <summary>
 /// Deserializes any tables defined in the specified <see cref="XContainer"/> object into the specified <see cref="HyperDbSchema"/> object.
 /// </summary>
 /// <param name="parent">The <see cref="XContainer"/> object containing the XML definitions for the columns.</param>
 /// <param name="table">The <see cref="HyperDbTable"/> object to receive the deserialized columns.</param>
 private void DeserializeColumns(XContainer parent, HyperDbTable table)
 {
     foreach (var columnElement in parent.Elements(_hyperDbXmlNamespace + "column"))
     {
         table.Columns.Add(new HyperDbColumn()
         {
             Name         = GetRequiredAttribute(columnElement, "name"),
             Type         = GetRequiredAttribute(columnElement, "type"),
             MaxLength    = GetOptionalAttributeInt32(columnElement, "maxLength", null),
             Decimals     = GetOptionalAttributeInt32(columnElement, "decimals", null),
             IsNullable   = GetOptionalAttributeBoolean(columnElement, "nullable", null),
             DefaultValue = GetOptionalAttribute(columnElement, "defaultValue", null)
         });
     }
 }
        /// <summary>
        /// Deserializes any constraints defined in the specified <see cref="XContainer"/> object into the specified <see cref="HyperDbSchema"/> object.
        /// </summary>
        /// <param name="parent">The <see cref="XContainer"/> object containing the XML definitions for the constraints.</param>
        /// <param name="table">The <see cref="HyperDbTable"/> object to receive the deserialized constraints.</param>
        private void DeserializeConstraints(XContainer parent, HyperDbTable table)
        {
            var primaryKeyElement = parent.Element(_hyperDbXmlNamespace + "primaryKey");

            if (primaryKeyElement != null)
            {
                DeserializePrimaryKey(primaryKeyElement, table);
            }

            var foreignKeyCollectionElement = parent.Element(_hyperDbXmlNamespace + "foreignKeys");

            if (foreignKeyCollectionElement != null)
            {
                DeserializeForeignKeys(foreignKeyCollectionElement, table);
            }
        }
        /// <summary>
        /// Deserializes any primary key columns defined in the specified <see cref="XElement"/> object into the specified <see cref="HyperDbSchema"/> object.
        /// </summary>
        /// <param name="parent">The <see cref="XElement"/> object containing the XML definitions for the primary key columns.</param>
        /// <param name="table">The <see cref="HyperDbTable"/> object to receive the deserialized primary key columns.</param>
        private void DeserializePrimaryKey(XElement parent, HyperDbTable table)
        {
            var scriptWriterType = _defaultDbPrimaryKeyScriptWriterType;
            var scriptWriterName = GetOptionalAttribute(parent, "scriptWriter", null);

            if (!string.IsNullOrWhiteSpace(scriptWriterName))
            {
                if (_scriptWriterTypes.ContainsKey(scriptWriterName))
                {
                    scriptWriterType = _scriptWriterTypes[scriptWriterName];
                }
                else
                {
                    throw new HyperDbSchemaConfigurationException("Invalid " + typeof(IDbPrimaryKeyScriptWriter).Name + " was specified for table " + table.Name + ".");
                }
            }

            table.PrimaryKey = new HyperDbPrimaryKey
            {
                PrimaryKeyName = GetOptionalAttribute(parent, "name", null),
                TableSource    = table
            };

            // Per our XML schema, if we have a primaryKey element, we will always have at least one keyColumn element
            foreach (var keyColumnElement in parent.Elements(_hyperDbXmlNamespace + "keyColumn"))
            {
                table.PrimaryKey.KeyColumns.Add(
                    GetRequiredAttribute(keyColumnElement, "name")
                    );
            }

            // Fill in our script writer
            var scriptWriterObject = CreateInstanceAs <IDbPrimaryKeyScriptWriter>(scriptWriterType);

            scriptWriterObject.Source     = table.PrimaryKey;
            table.PrimaryKey.ScriptWriter = scriptWriterObject;
        }