/// <summary>
		/// Returns the full schema of the database including tables, indexes, foreign keys, etc.
		/// </summary>
		/// <remarks>
		/// It's very slow for large databases
		/// </remarks>
		public static DataBaseSchema Load(DataBase database, string schemaProvider)
		{
			DataBaseSchema schema = new DataBaseSchema();
			DatabaseSchema schemaReader;

			using (var dbReader = new DatabaseSchemaReader.DatabaseReader(database.ConnectionString, schemaProvider))
			{
				dbReader.AllTables();
				dbReader.AllViews();

				try
				{
					dbReader.AllStoredProcedures();
				}
				catch { }

				try
				{
					dbReader.AllUsers();
				}
				catch { }

				schemaReader = dbReader.DatabaseSchema;
			}

			foreach (DatabaseTable dbt in schemaReader.Tables)
			{
				if (dbt.PrimaryKeyColumn == null)
				{
					continue;
				}

				dbt.PrimaryKeyColumn.AddIdentity();

				var table = new Table()
				{
					Name = dbt.Name,
					DataBase = schema,
					IdentityIncrement = dbt.PrimaryKeyColumn.IdentityDefinition.IdentityIncrement,
					IdentitySeed = dbt.PrimaryKeyColumn.IdentityDefinition.IdentitySeed,
				};

				schema.Tables.Add(table);
			}

			foreach (DatabaseTable dbt in schemaReader.Tables)
			{
				if (dbt.PrimaryKeyColumn == null)
				{
					continue;
				}

				var table = schema[dbt.Name];

				foreach (DatabaseColumn dbc in dbt.Columns)
				{
					Column column = new Column()
					{
						Name = dbc.Name,
						Table = table,
						Description = dbc.Description,
						IsNullable = dbc.Nullable,
						IsAutoNumber = dbc.IsAutoNumber,
						ComputedDefinition = dbc.ComputedDefinition,
						DefaultValue = dbc.DefaultValue,
						IsPrimaryKey = dbc.IsPrimaryKey,
						Length = (uint?) dbc.Length,
						Ordinal = dbc.Ordinal,
						Precision = dbc.Precision,
						Scale = dbc.Scale,
					};

						
					if (dbc.DataType != null)
					{
						column.DbType = DbTypeMapper.Parse(dbc.DataType.GetNetType());
					}
					else
					{
						if(dbc.DbDataType.StartsWith("varchar"))
						{
							column.DbType = DbType.AnsiString;
						}
						else if (dbc.DbDataType.StartsWith("int"))
						{
							column.DbType = DbType.Int32;
						}
						else if (dbc.DbDataType.StartsWith("decimal"))
						{
							column.DbType = DbType.Decimal;
						}
						else if (dbc.DbDataType.StartsWith("datetime"))
						{
							column.DbType = DbType.DateTime;
						}
						else if (dbc.DbDataType.StartsWith("money"))
						{
							column.DbType = DbType.Currency;
						}
						else if (dbc.DbDataType.StartsWith("char"))
						{
							column.DbType = DbType.AnsiStringFixedLength;
						}
						else if (dbc.DbDataType.StartsWith("text"))
						{
							column.DbType = DbType.AnsiString;
						}
					}

					table.Columns.Add(column);
				}

				foreach (DatabaseIndex dbi in dbt.Indexes)
				{
					Index index = new Index()
					{
						Name = dbi.Name,
						Table = table,
						Direction = SortDirection.Ascending,
						Unique = dbi.IsUnique,
					};
					
					foreach (DatabaseColumn dbc in dbi.Columns)
					{
						index.Columns.Add(table[dbc.Name]);
					}
					
					table.Indexes.Add(index);
				}

				foreach (DatabaseTrigger dbtr in dbt.Triggers)
				{
					DataBaseOperation operation = DataBaseOperation.Insert;
					Enum.TryParse<DataBaseOperation>(dbtr.TriggerEvent, true, out operation);

					Trigger trigger = new Trigger()
					{
						TriggerBody = dbtr.TriggerBody,
						TriggerEvent = operation,
						Table = table,
					};

					table.Triggers.Add(trigger);
				}

				foreach (DatabaseConstraint dbcons in dbt.CheckConstraints)
				{
					if (dbcons.ConstraintType == ConstraintType.Check)
					{
						CheckConstraint constraint = new CheckConstraint()
						{
							Expression = dbcons.Expression,
							Table = table,
						};
						
						table.CheckConstraints.Add(constraint);
					}
					else if (dbcons.ConstraintType == ConstraintType.ForeignKey)
					{
						ForeignKey foreignKey = new ForeignKey()
						{
							Name= dbcons.Name,
							DeleteAction = schema.ParseConstraintAction(dbcons.DeleteRule),
							UpdateAction =schema.ParseConstraintAction(dbcons.UpdateRule),
							RemoteTable = schema[dbcons.RefersToTable],
							Table = table,
						};

						var referencedColumns = dbcons.ReferencedColumns(schemaReader).ToArray();
						for (int i = 0; i < dbcons.Columns.Count; i++)
						{
							foreignKey.Columns.Add(new Tuple<Column,Column>(table[dbcons.Columns[i]], foreignKey.RemoteTable[referencedColumns[i]]));
						}

						table.ForeignKeys.Add(foreignKey);
					}
				}
			}

			foreach (DatabaseView dbv in schemaReader.Views)
			{
				View view = new View()
				{
					Name = dbv.Name,
					Command = dbv.Sql,
					Description = dbv.Description,
					DataBase = schema,
				};

				schema.Views.Add(view);
			}

			foreach (DatabaseUser dbu in schemaReader.Users)
			{
				User user = new User()
				{
					Name = dbu.Name,
					DataBase = schema,
				};

				schema.Users.Add(user);
			}

			return schema;
		}
		/// <summary>
		/// Returns the Sql sentence for creating an index in the specified TypeMap<T>. Can be used when creatingt a table or after creation, for adding an index
		/// </summary>
		/// <param name="index">Index that will be added</param>
		protected override Command IndexDefinition(Index index)
		{
			//local vars
			Command sql = new Command();

			//Creating the sql 
			sql += 
				"CONSTRAINT " + 
				EncloseName(index.Name) + 
				" UNIQUE (";

			//add columns to sql
			foreach (var column in index.Columns)
			{
				sql += this.EncloseName(column.Name) + ", ";
			}

			//Enclosing the field lists
			sql.Script = sql.Script.TrimEnd(',', ' ');
			sql += ")";

			return sql;
		}
		/// <summary>
		/// Returns the Sql sentence for dropping an index in the specified Table
		/// </summary>
		/// <param name="index">Index that will be dropped</param>
		/// <returns>Sql query for dropping the index</returns>
		public virtual Command Drop(Index index)
		{
			return "ALTER TABLE " + EncloseName(index.Table.Name) + " DROP INDEX " + EncloseName(index.Name);
		}
		/// <summary>
		/// Returns the Sql sentence for adding an index in the specified Table
		/// </summary>
		/// <param name="index">Index that will be added</param>
		/// <returns>Sql query for adding the index</returns>
		public virtual Command Create(Index index)
		{
			return "ALTER TABLE " + EncloseName(index.Table.Name) + " ADD " + IndexDefinition(index);
		}
		/// <summary>
		/// Returns the Sql sentence for creating an index in the specified Table. Can be used when creating a table or after creation, for adding an index
		/// </summary>
		/// <param name="index">Index that will be added</param>
		protected virtual Command IndexDefinition(Index index)
		{
			//local vars
			string sql = string.Empty;

			//Creating the sql 
			sql +=
				(index.Unique ? "UNIQUE " : string.Empty) +
				"INDEX " +
				EncloseName(index.Name) +
				"(";

			//add columns to sql
			foreach (Column column in index.Columns)
			{
				sql += EncloseName(column.Name) + ", ";
			}

			//Enclosing the field lists
			sql = sql.TrimEnd(',', ' ');
			sql += ")";

			return sql;
		}
		/// <summary>
		/// Returns the Sql sentence for creating an index in the specified TypeMap<T>. Can be used when creating a table or after creation, for adding an index
		/// </summary>
		/// <param name="index">Index that will be added</param>
		protected override Command IndexDefinition(Index index)
		{
			//this method should not be used on Access databases
			return string.Empty;
		}
		/// <summary>
		/// Returns all the scripts necesary to create all indexes in a datatype
		/// </summary>
		/// <param name="dtype">TypeMap<T> which indexes will be generated</param>
		/// <param name="inherits">If true, scripts for all base DataTypes indexes are generated too</param>
		/// <returns>Sql script for creating indexes</returns>
		public override Command Create(Index index)
		{
			string sql = string.Empty;
			
			sql +=
				"CREATE " +
				(index.Unique ? "UNIQUE " : string.Empty) +
				"INDEX " +
				this.EncloseName(index.Name) +
				" ON " +
				EncloseName(index.Table.Name) +
				"(";

			//add columns to sql
			foreach (var c in index.Columns)
			{
				sql += this.EncloseName(c.Name) + ", ";
			}

			//Enclosing the field lists
			sql = sql.TrimEnd(',', ' ');
			sql += ")";

			return sql;
		}