/// <summary>
        /// Creates a new database using DbSchema.
        /// </summary>
        /// <param name="databaseName">a name of the new database to create</param>
        /// <param name="dbSchemaFolderPath">the path to the folder that contains gauge schema files
        /// (all files loaded in order to support plugins that may require their own tables)</param>
        /// <param name="forExtensions">a list of extensions Guid's to include in schema;
        /// if null or empty all schema files will be included</param>
        /// <remarks>After creating a new database the <see cref="CurrentDatabase">CurrentDatabase</see>
        /// property should be set to the new database name.</remarks>
        /// <exception cref="ArgumentNullException">databaseName is not specified</exception>
        /// <exception cref="ArgumentNullException">dbSchemaFolderPath is not specified</exception>
        /// <exception cref="ArgumentException">dbSchemaFolderPath contains one or more invalid characters
        /// as defined by InvalidPathChars.</exception>
        /// <exception cref="PathTooLongException">The specified dbSchemaFolderPath, file name, or both exceed
        /// the system-defined maximum length. For example, on Windows-based platforms,
        /// paths must be less than 248 characters, and file names must be less than 260 characters.</exception>
        /// <exception cref="DirectoryNotFoundException">The specified dbSchemaFolderPath is invalid
        /// (for example, it is on an unmapped drive).</exception>
        /// <exception cref="IOException">An I/O error occurred while opening the file.</exception>
        /// <exception cref="UnauthorizedAccessException">The caller does not have the required permission.</exception>
        /// <exception cref="FileNotFoundException">The file specified in dbSchemaFolderPath was not found.</exception>
        /// <exception cref="NotSupportedException">dbSchemaFolderPath is in an invalid format.</exception>
        /// <exception cref="SecurityException">The caller does not have the required permission.</exception>
        public Task CreateDatabaseAsync(string dbSchemaFolderPath, Guid[] forExtensions = null)
        {
            if (_agent.IsTransactionInProgress)
            {
                throw new InvalidOperationException(Properties.Resources.SqlAgentBase_CannotCreateDatabase);
            }
            if (dbSchemaFolderPath.IsNullOrWhiteSpace())
            {
                throw new ArgumentNullException(nameof(dbSchemaFolderPath));
            }
            if (!System.IO.Directory.Exists(dbSchemaFolderPath))
            {
                throw new ArgumentException(string.Format(
                                                Properties.Resources.DbSchemaFolderDoesNotExistException, dbSchemaFolderPath), nameof(dbSchemaFolderPath));
            }

            var dbSchema = new DbSchema();

            dbSchema.LoadXmlFolder(dbSchemaFolderPath, forExtensions);

            if (dbSchema.Tables.Count < 1)
            {
                throw new ArgumentException(Properties.Resources.SqlAgentBase_GaugeSchemaEmpty);
            }

            return(CreateDatabaseAsync(dbSchema));
        }
        /// <summary>
        /// Compares the current database definition to the gauge definition definition read from
        /// the file specified and returns a list of DbSchema errors, i.e. inconsistencies found
        /// and SQL statements to repair them.
        /// </summary>
        /// <param name="dbSchemaFolderPath">the path to the folder that contains gauge schema files
        /// (all files loaded in order to support plugins that may require their own tables)</param>
        /// <param name="forExtensions">a list of extensions Guid's to include in schema;
        /// if null or empty all schema files will be included</param>
        /// <param name="cancellationToken">a cancelation token (if any)</param>
        /// <exception cref="ArgumentNullException">databaseName is not specified</exception>
        /// <exception cref="ArgumentNullException">dbSchemaFolderPath is not specified</exception>
        /// <exception cref="ArgumentException">dbSchemaFolderPath contains one or more invalid characters
        /// as defined by InvalidPathChars.</exception>
        /// <exception cref="PathTooLongException">The specified dbSchemaFolderPath, file name, or both exceed
        /// the system-defined maximum length. For example, on Windows-based platforms,
        /// paths must be less than 248 characters, and file names must be less than 260 characters.</exception>
        /// <exception cref="DirectoryNotFoundException">The specified dbSchemaFolderPath is invalid
        /// (for example, it is on an unmapped drive).</exception>
        /// <exception cref="IOException">An I/O error occurred while opening the file.</exception>
        /// <exception cref="UnauthorizedAccessException">The caller does not have the required permission.</exception>
        /// <exception cref="FileNotFoundException">The file specified in dbSchemaFolderPath was not found.</exception>
        /// <exception cref="NotSupportedException">dbSchemaFolderPath is in an invalid format.</exception>
        /// <exception cref="SecurityException">The caller does not have the required permission.</exception>
        public async Task <List <DbSchemaError> > GetDbSchemaErrorsAsync(string dbSchemaFolderPath,
                                                                         Guid[] forExtensions, CancellationToken cancellationToken)
        {
            if (dbSchemaFolderPath.IsNullOrWhiteSpace())
            {
                throw new ArgumentNullException(nameof(dbSchemaFolderPath));
            }
            if (!System.IO.Directory.Exists(dbSchemaFolderPath))
            {
                throw new ArgumentException(string.Format(
                                                Properties.Resources.DbSchemaFolderDoesNotExistException, dbSchemaFolderPath), nameof(dbSchemaFolderPath));
            }

            var gaugeSchema = new DbSchema();

            gaugeSchema.LoadXmlFolder(dbSchemaFolderPath, forExtensions);

            if (gaugeSchema.Tables.Count < 1)
            {
                throw new ArgumentException(Properties.Resources.SqlAgentBase_GaugeSchemaEmpty);
            }

            var actualSchema = await GetDbSchemaAsync(cancellationToken).ConfigureAwait(false);

            return(this.GetDbSchemaErrors(gaugeSchema, actualSchema));
        }
        /// <summary>
        /// Creates a clone of the database using another SqlAgent.
        /// </summary>
        /// <param name="cloneManager">an SQL schema manager to use when creating clone database</param>
        /// <param name="schemaToUse">a database schema to use (enforce),
        /// if null the schema ir read from the database cloned</param>
        /// <param name="ct">a cancelation token (if any)</param>
        /// <param name="progress">a progress callback (if any)</param>
        public async Task CloneDatabase(SchemaManagerBase cloneManager, DbSchema schemaToUse,
                                        IProgress <DbCloneProgressArgs> progress, CancellationToken ct)
        {
            if (cloneManager.IsNull())
            {
                throw new ArgumentNullException(nameof(cloneManager));
            }

            progress?.Report(new DbCloneProgressArgs(DbCloneProgressArgs.Stage.FetchingSchema, string.Empty, 0));

            if (schemaToUse.IsNull())
            {
                schemaToUse = await GetDbSchemaAsync(ct).ConfigureAwait(false);
            }

            if (CloneCanceled(progress, ct))
            {
                return;
            }

            progress?.Report(new DbCloneProgressArgs(DbCloneProgressArgs.Stage.CreatingSchema, string.Empty, 0));

            await cloneManager.CreateDatabaseAsync(schemaToUse).ConfigureAwait(false);

            if (CloneCanceled(progress, ct))
            {
                return;
            }

            await cloneManager.Agent.ExecuteInTransactionAsync(async (cancellationToken) =>
            {
                await cloneManager.DisableForeignKeysForCurrentTransactionAsync().ConfigureAwait(false);
                await CopyData(schemaToUse, cloneManager, progress, cancellationToken).ConfigureAwait(false);
            }, ct).ConfigureAwait(false);

            if (!ct.IsCancellationRequested)
            {
                progress?.Report(
                    new DbCloneProgressArgs(DbCloneProgressArgs.Stage.Completed, string.Empty, 100));
            }
        }
 /// <summary>
 /// Copies table data from the current SqlAgent instance to the target SqlAgent instance.
 /// </summary>
 /// <param name="schema">a schema of the database to copy the data</param>
 /// <param name="targetManager">the target Sql schema manager to copy the data to</param>
 /// <remarks>Required for <see cref="CloneDatabase">CloneDatabase</see> infrastructure.
 /// Basicaly iterates tables, selects data, creates an IDataReader for the table and passes it to the
 /// <see cref="InsertTableData">InsertTableData</see> method of the target SqlAgent.</remarks>
 protected abstract Task CopyData(DbSchema schema, SchemaManagerBase targetManager,
                                  IProgress <DbCloneProgressArgs> progress, CancellationToken ct);
 /// <summary>
 /// A method that should do the actual new database creation.
 /// </summary>
 /// <param name="databaseName">a name of the new database to create</param>
 /// <param name="dbSchema">a DbSchema to use for the new database</param>
 /// <remarks>After creating a new database the <see cref="CurrentDatabase">CurrentDatabase</see>
 /// property should be set to the new database name.</remarks>
 public abstract Task CreateDatabaseAsync(DbSchema dbSchema);
 /// <summary>
 /// Gets an SQL script to create a database for the dbSchema specified.
 /// </summary>
 /// <param name="dbSchema">the database schema to get the create database script for</param>
 public abstract string GetCreateDatabaseSql(DbSchema dbSchema);
 /// <summary>
 /// Compares the actualSchema definition to the gaugeSchema definition and returns
 /// a list of DbSchema errors, i.e. inconsistencies found and SQL statements to repair them.
 /// </summary>
 /// <param name="gaugeSchema">the gauge schema definition to compare the actualSchema against</param>
 /// <param name="actualSchema">the schema to check for inconsistencies (and repair)</param>
 public abstract List <DbSchemaError> GetDbSchemaErrors(DbSchema gaugeSchema, DbSchema actualSchema);