public bool MigrationApplied(string schema, StoreMigration migration)
        {
            if (!_storeDatabaseDriver.TableExists(schema, VERSION_TABLE))
            {
                //_storeDatabaseDriver.CreateTable<MigrationVersionEntity>(schema, VERSION_TABLE);
                return(false);
            }

            var versionRecord = _storeDatabaseDriver.Find <MigrationVersionEntity>(schema, VERSION_TABLE, VERSION_ID, migration.Version);

            if (versionRecord.Any())
            {
                if (migration.Version == INITIAL_VERSION && versionRecord.First().VersionKey != migration.VersionKey)
                {
                    throw new DataMigrationException(
                              $"Incompatible initial migration already applied, expected key is {migration.VersionKey} but key in the database is {versionRecord.First().VersionKey}. Clear target database and try again.");
                }

                // edititng incrementatl migration can be partially applied
                if (/*migration.Version != INITIAL_VERSION &&*/ migration.Commands.Length != versionRecord.First().NextIndex)
                {
                    return(false);
                }

                return(true);
            }

            return(false);
        }
        public string MigrationToString(StoreMigration migration)
        {
            var sb = new StringBuilder();

            foreach (var cmd in migration.Commands)
            {
                switch (cmd.Operation)
                {
                // ToDo: add foreign key constraints
                case MigrationOperation.CreateTable:
                    GenerateTableScript(sb, cmd.Table);
                    break;

                case MigrationOperation.AddColumn:
                    GenerateColumnScript(sb, cmd.TableName, cmd.Column, "CREATE");
                    break;

                case MigrationOperation.AlterColumn:
                    GenerateColumnScript(sb, cmd.TableName, cmd.Column, "ALTER");
                    break;

                case MigrationOperation.AlterColumnName:
                    GenerateColumnRenameScript(sb, cmd.TableName, cmd.ColumnName, cmd.NewValue);
                    break;

                case MigrationOperation.CreateSchema:
                    sb.AppendLine($"CREATE SCHEMA ({cmd.SchemaName})");
                    break;

                case MigrationOperation.AlterSchemaName:
                    sb.AppendLine($"ALTER SCHEMA {cmd.SchemaName} ({cmd.NewValue})");
                    break;

                // V1.0+ migrations
                case MigrationOperation.DeleteTable:
                    sb.AppendLine($"DELETE TABLE ({cmd.TableName})");
                    break;

                case MigrationOperation.AlterTableName:
                    sb.AppendLine($"RENAME TABLE {cmd.TableName} ({cmd.NewValue})");
                    break;

                case MigrationOperation.DeleteColumn:
                    sb.AppendLine($"DELETE COLUMN ({cmd.TableName}.{cmd.ColumnName})");
                    break;

                default:
                    sb.AppendLine($"{cmd.OperationCode}");
                    break;
                }

                sb.AppendLine();
            }

            return(sb.ToString());
        }
        public void ApplyMigration(string schema, StoreMigration migration)
        {
            int nextIndex = 0;

            try
            {
                // try read version - table maybe doesn't exist
                var lastVersionRecord = _storeDatabaseDriver.Find <MigrationVersionEntity>(schema, VERSION_TABLE, VERSION_ID, migration.Version);

                if (lastVersionRecord.Any())
                {
                    nextIndex = lastVersionRecord.First().NextIndex;
                }
            }
            catch { }

            // skip migrations that already applied;
            var commands = migration.Commands.Skip(nextIndex);

            foreach (var command in commands)
            {
                nextIndex++;

                switch (command.Operation)
                {
                // ToDo: add foreign key constraints
                case MigrationOperation.CreateSchema:
                    _storeDatabaseDriver.CreateSchema(command.SchemaName);
                    _storeDatabaseDriver.CreateTable <MigrationVersionEntity>(schema, VERSION_TABLE);
                    // this operation is not considered as object operation
                    //nextIndex--;
                    break;

                case MigrationOperation.AlterSchemaName:
                    // https://www.sqlservercentral.com/articles/renaming-a-schema-in-sql-server
                    throw new NotImplementedException("SQL Server doesn't support simple schema rename. ");

                case MigrationOperation.CreateTable:
                    _storeDatabaseDriver.CreateTable(command.SchemaName, command.Table);
                    break;

                // V1.0+ migrations
                case MigrationOperation.DeleteTable:
                    _storeDatabaseDriver.DeleteTable(command.SchemaName, command.TableName);
                    break;

                case MigrationOperation.AlterTableName:
                    _storeDatabaseDriver.RenameTable(command.SchemaName, command.TableName, command.NewValue);
                    break;

                case MigrationOperation.AddColumn:
                    _storeDatabaseDriver.AddColumn(command.SchemaName, command.TableName, command.Column);
                    break;

                case MigrationOperation.DeleteColumn:
                    _storeDatabaseDriver.DeleteColumn(command.SchemaName, command.TableName, command.ColumnName, command.Column);
                    break;

                case MigrationOperation.AlterColumnName:
                    _storeDatabaseDriver.RenameColumn(command.SchemaName, command.TableName, command.ColumnName, command.NewValue);
                    break;

                case MigrationOperation.AlterColumn:
                    _storeDatabaseDriver.AlterColumn(command.SchemaName, command.TableName, command.Column);
                    break;

                default:
                    throw new NotImplementedException();
                }
            }

            // save to version table
            var vesionRecord = new MigrationVersionEntity {
                Applied = DateTime.UtcNow, Version = migration.Version, VersionKey = migration.VersionKey, NextIndex = nextIndex
            };

            var existingRecord = _storeDatabaseDriver.Find <MigrationVersionEntity>(schema, VERSION_TABLE, VERSION_ID, migration.Version);

            if (existingRecord.Any())
            {
                _storeDatabaseDriver.Update(schema, vesionRecord, VERSION_TABLE);
            }
            else
            {
                _storeDatabaseDriver.Insert(schema, vesionRecord, migration.Version, VERSION_TABLE);
            }
        }