public static SqlPreCommand?SynchronizeScriptReplacing <N, O>( Replacements replacements, string replacementsKey, Spacing spacing, Dictionary <string, N> newDictionary, Dictionary <string, O> oldDictionary, Func <string, N, SqlPreCommand?>?createNew, Func <string, O, SqlPreCommand?>?removeOld, Func <string, N, O, SqlPreCommand?>?mergeBoth) where O : class where N : class { replacements.AskForReplacements( oldDictionary.Keys.ToHashSet(), newDictionary.Keys.ToHashSet(), replacementsKey); var repOldDictionary = replacements.ApplyReplacementsToOld(oldDictionary, replacementsKey); return(SynchronizeScript(spacing, newDictionary, repOldDictionary, createNew, removeOld, mergeBoth)); }
public static void SynchronizeReplacing <O, N>( Replacements replacements, string replacementsKey, Dictionary <string, O> oldDictionary, Dictionary <string, N> newDictionary, Action <string, O> removeOld, Action <string, N> createNew, Action <string, O, N> merge) where O : class where N : class { replacements.AskForReplacements( oldDictionary.Keys.ToHashSet(), newDictionary.Keys.ToHashSet(), replacementsKey); var repOldDictionary = replacements.ApplyReplacementsToOld(oldDictionary, replacementsKey); HashSet <string> set = new HashSet <string>(); set.UnionWith(repOldDictionary.Keys); set.UnionWith(newDictionary.Keys); foreach (var key in set) { var oldVal = repOldDictionary.TryGetC(key); var newVal = newDictionary.TryGetC(key); if (oldVal == null) { createNew(key, newVal !); } else if (newVal == null) { removeOld(key, oldVal); } else { merge(key, oldVal, newVal); } } }
public static void SynchronizeRoles(XDocument doc) { Table table = Schema.Current.Table(typeof(RoleEntity)); TableMList relationalTable = table.TablesMList().Single(); Dictionary<string, XElement> rolesXml = doc.Root.Element("Roles").Elements("Role").ToDictionary(x => x.Attribute("Name").Value); { Dictionary<string, RoleEntity> rolesDic = Database.Query<RoleEntity>().ToDictionary(a => a.ToString()); Replacements replacements = new Replacements(); replacements.AskForReplacements(rolesDic.Keys.ToHashSet(), rolesXml.Keys.ToHashSet(), "Roles"); rolesDic = replacements.ApplyReplacementsToOld(rolesDic, "Roles"); Console.WriteLine("Part 1: Syncronize roles without relationships"); var roleInsertsDeletes = Synchronizer.SynchronizeScript(rolesXml, rolesDic, (name, xelement) => table.InsertSqlSync(new RoleEntity { Name = name }, includeCollections: false), (name, role) => SqlPreCommand.Combine(Spacing.Simple, new SqlPreCommandSimple("DELETE {0} WHERE {1} = {2} --{3}" .FormatWith(relationalTable.Name, ((IColumn)relationalTable.Field).Name.SqlEscape(), role.Id, role.Name)), table.DeleteSqlSync(role)), (name, xElement, role) => { var oldName = role.Name; role.Name = name; role.MergeStrategy = xElement.Attribute("MergeStrategy")?.Let(t => t.Value.ToEnum<MergeStrategy>()) ?? MergeStrategy.Union; return table.UpdateSqlSync(role, includeCollections: false, comment: oldName); }, Spacing.Double); if (roleInsertsDeletes != null) { SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN ROLE SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), roleInsertsDeletes, new SqlPreCommandSimple("-- END ROLE SYNC SCRIPT")).OpenSqlFileRetry(); Console.WriteLine("Press [Enter] when executed..."); Console.ReadLine(); } else { SafeConsole.WriteLineColor(ConsoleColor.Green, "Already syncronized"); } } { Console.WriteLine("Part 2: Syncronize roles relationships"); Dictionary<string, RoleEntity> rolesDic = Database.Query<RoleEntity>().ToDictionary(a => a.ToString()); var roleRelationships = Synchronizer.SynchronizeScript(rolesXml, rolesDic, (name, xelement) => { throw new InvalidOperationException("No new roles should be at this stage. Did you execute the script?"); }, (name, role) => { throw new InvalidOperationException("No old roles should be at this stage. Did you execute the script?"); }, (name, xElement, role) => { var should = xElement.Attribute("Contains").Value.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries); var current = role.Roles.Select(a=>a.ToString()); if(should.OrderBy().SequenceEqual(current.OrderBy())) return null; role.Roles = should.Select(rs => rolesDic.GetOrThrow(rs).ToLite()).ToMList(); return table.UpdateSqlSync(role); }, Spacing.Double); if (roleRelationships != null) { SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN ROLE SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), roleRelationships, new SqlPreCommandSimple("-- END ROLE SYNC SCRIPT")).OpenSqlFileRetry(); Console.WriteLine("Press [Enter] when executed..."); Console.ReadLine(); } else { SafeConsole.WriteLineColor(ConsoleColor.Green, "Already syncronized"); } } }
static SqlPreCommand SynchronizeEnumsScript(Replacements replacements) { Schema schema = Schema.Current; List<SqlPreCommand> commands = new List<SqlPreCommand>(); foreach (var table in schema.Tables.Values) { Type enumType = EnumEntity.Extract(table.Type); if (enumType != null) { IEnumerable<Entity> should = EnumEntity.GetEntities(enumType); Dictionary<string, Entity> shouldByName = should.ToDictionary(a => a.ToString()); List<Entity> current = Administrator.TryRetrieveAll(table.Type, replacements); Dictionary<string, Entity> currentByName = current.ToDictionaryEx(a => a.toStr, table.Name.Name); string key = Replacements.KeyEnumsForTable(table.Name.Name); replacements.AskForReplacements(currentByName.Keys.ToHashSet(), shouldByName.Keys.ToHashSet(), key); currentByName = replacements.ApplyReplacementsToOld(currentByName, key); var mix = shouldByName.JoinDictionary(currentByName, (n, s, c) => new { s, c }).Where(a => a.Value.s.id != a.Value.c.id).ToDictionary(); HashSet<PrimaryKey> usedIds = current.Select(a => a.Id).ToHashSet(); Dictionary<string, Entity> middleByName = mix.Where(kvp => usedIds.Contains(kvp.Value.s.Id)).ToDictionary(kvp => kvp.Key, kvp => Clone(kvp.Value.c)); if (middleByName.Any()) { var moveToAux = SyncEnums(schema, table, currentByName.Where(a => middleByName.ContainsKey(a.Key)).ToDictionary(), middleByName); if (moveToAux != null) commands.Add(moveToAux); } var com = SyncEnums(schema, table, currentByName.Where(a=>!middleByName.ContainsKey(a.Key)).ToDictionary(), shouldByName.Where(a=>!middleByName.ContainsKey(a.Key)).ToDictionary()); if (com != null) commands.Add(com); if (middleByName.Any()) { var backFromAux = SyncEnums(schema, table, middleByName, shouldByName.Where(a => middleByName.ContainsKey(a.Key)).ToDictionary()); if (backFromAux != null) commands.Add(backFromAux); } } } return SqlPreCommand.Combine(Spacing.Double, commands.ToArray()); }
public static SqlPreCommand SynchronizeTablesScript(Replacements replacements) { Schema s = Schema.Current; Dictionary<string, ITable> model = s.GetDatabaseTables().Where(t => !s.IsExternalDatabase(t.Name.Schema.Database)).ToDictionaryEx(a => a.Name.ToString(), "schema tables"); HashSet<SchemaName> modelSchemas = model.Values.Select(a => a.Name.Schema).Where(a => !SqlBuilder.SystemSchemas.Contains(a.Name)).ToHashSet(); Dictionary<string, DiffTable> database = DefaultGetDatabaseDescription(s.DatabaseNames()); HashSet<SchemaName> databaseSchemas = DefaultGetSchemas(s.DatabaseNames()); if (SimplifyDiffTables != null) SimplifyDiffTables(database); replacements.AskForReplacements(database.Keys.ToHashSet(), model.Keys.ToHashSet(), Replacements.KeyTables); database = replacements.ApplyReplacementsToOld(database, Replacements.KeyTables); Dictionary<ITable, Dictionary<string, Index>> modelIndices = model.Values .ToDictionary(t => t, t => t.GeneratAllIndexes().ToDictionaryEx(a => a.IndexName, "Indexes for {0}".FormatWith(t.Name))); model.JoinDictionaryForeach(database, (tn, tab, diff) => { var key = Replacements.KeyColumnsForTable(tn); replacements.AskForReplacements(diff.Columns.Keys.ToHashSet(), tab.Columns.Keys.ToHashSet(), key); diff.Columns = replacements.ApplyReplacementsToOld(diff.Columns, key); diff.Indices = ApplyIndexAutoReplacements(diff, tab, modelIndices[tab]); }); Func<ObjectName, ObjectName> ChangeName = (ObjectName objectName) => { string name = replacements.Apply(Replacements.KeyTables, objectName.ToString()); return model.TryGetC(name)?.Name ?? objectName; }; Func<ObjectName, SqlPreCommand> DeleteAllForeignKey = tableName => { var dropFks = (from t in database.Values from c in t.Columns.Values where c.ForeignKey != null && c.ForeignKey.TargetTable.Equals(tableName) select SqlBuilder.AlterTableDropConstraint(t.Name, c.ForeignKey.Name)).Combine(Spacing.Simple); if (dropFks == null) return null; return SqlPreCommand.Combine(Spacing.Simple, new SqlPreCommandSimple("---In order to remove the PK of " + tableName.Name), dropFks); }; using (replacements.WithReplacedDatabaseName()) { SqlPreCommand createSchemas = Synchronizer.SynchronizeScriptReplacing(replacements, "Schemas", modelSchemas.ToDictionary(a => a.ToString()), databaseSchemas.ToDictionary(a => a.ToString()), (_, newSN) => SqlBuilder.CreateSchema(newSN), null, (_, newSN, oldSN) => newSN.Equals(oldSN) ? null : SqlBuilder.CreateSchema(newSN), Spacing.Double); //use database without replacements to just remove indexes SqlPreCommand dropStatistics = Synchronizer.SynchronizeScript(model, database, null, (tn, dif) => SqlBuilder.DropStatistics(tn, dif.Stats), (tn, tab, dif) => { var removedColums = dif.Columns.Keys.Except(tab.Columns.Keys).ToHashSet(); return SqlBuilder.DropStatistics(tn, dif.Stats.Where(a => a.Columns.Any(removedColums.Contains)).ToList()); }, Spacing.Double); SqlPreCommand dropIndices = Synchronizer.SynchronizeScript(model, database, null, (tn, dif) => dif.Indices.Values.Where(ix => !ix.IsPrimary).Select(ix => SqlBuilder.DropIndex(dif.Name, ix)).Combine(Spacing.Simple), (tn, tab, dif) => { Dictionary<string, Index> modelIxs = modelIndices[tab]; var removedColums = dif.Columns.Keys.Except(tab.Columns.Keys).ToHashSet(); var changes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, null, (i, dix) => dix.Columns.Any(removedColums.Contains) || dix.IsControlledIndex ? SqlBuilder.DropIndex(dif.Name, dix) : null, (i, mix, dix) => !dix.IndexEquals(dif, mix) ? SqlPreCommand.Combine(Spacing.Double, dix.IsPrimary ? DeleteAllForeignKey(dif.Name) : null, SqlBuilder.DropIndex(dif.Name, dix)) : null, Spacing.Simple); return changes; }, Spacing.Double); SqlPreCommand dropForeignKeys = Synchronizer.SynchronizeScript( model, database, null, (tn, dif) => dif.Columns.Values.Select(c => c.ForeignKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, c.ForeignKey.Name) : null) .Concat(dif.MultiForeignKeys.Select(fk => SqlBuilder.AlterTableDropConstraint(dif.Name, fk.Name))).Combine(Spacing.Simple), (tn, tab, dif) => SqlPreCommand.Combine(Spacing.Simple, Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, null, (cn, colDb) => colDb.ForeignKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeignKey.Name) : null, (cn, colModel, colDb) => colDb.ForeignKey == null ? null : colModel.ReferenceTable == null || colModel.AvoidForeignKey || !colModel.ReferenceTable.Name.Equals(ChangeName(colDb.ForeignKey.TargetTable)) ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeignKey.Name) : null, Spacing.Simple), dif.MultiForeignKeys.Select(fk => SqlBuilder.AlterTableDropConstraint(dif.Name, fk.Name)).Combine(Spacing.Simple)), Spacing.Double); SqlPreCommand tables = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlBuilder.CreateTableSql(tab), (tn, dif) => SqlBuilder.DropTable(dif.Name), (tn, tab, dif) => SqlPreCommand.Combine(Spacing.Simple, !object.Equals(dif.Name, tab.Name) ? SqlBuilder.RenameOrMove(dif, tab) : null, Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, (cn, tabCol) => SqlPreCommandSimple.Combine(Spacing.Simple, tabCol.PrimaryKey && dif.PrimaryKeyName != null ? SqlBuilder.DropPrimaryKeyConstraint(tab.Name) : null, AlterTableAddColumnDefault(tab, tabCol, replacements)), (cn, difCol) => SqlPreCommandSimple.Combine(Spacing.Simple, difCol.Default != null ? SqlBuilder.DropDefaultConstraint(tab.Name, difCol.Name) : null, SqlBuilder.AlterTableDropColumn(tab, cn)), (cn, tabCol, difCol) => SqlPreCommand.Combine(Spacing.Simple, difCol.Name == tabCol.Name ? null : SqlBuilder.RenameColumn(tab, difCol.Name, tabCol.Name), difCol.ColumnEquals(tabCol, ignorePrimaryKey: true) ? null : SqlPreCommand.Combine(Spacing.Simple, tabCol.PrimaryKey && !difCol.PrimaryKey && dif.PrimaryKeyName != null ? SqlBuilder.DropPrimaryKeyConstraint(tab.Name) : null, SqlBuilder.AlterTableAlterColumn(tab, tabCol), tabCol.SqlDbType == SqlDbType.NVarChar && difCol.SqlDbType == SqlDbType.NChar ? SqlBuilder.UpdateTrim(tab, tabCol) : null), difCol.DefaultEquals(tabCol) ? null : SqlPreCommand.Combine(Spacing.Simple, difCol.Default != null ? SqlBuilder.DropDefaultConstraint(tab.Name, tabCol.Name) : null, tabCol.Default != null ? SqlBuilder.AddDefaultConstraint(tab.Name, tabCol.Name, tabCol.Default) : null), UpdateByFkChange(tn, difCol, tabCol, ChangeName)), Spacing.Simple)), Spacing.Double); if (tables != null) tables.GoAfter = true; var tableReplacements = replacements.TryGetC(Replacements.KeyTables); if (tableReplacements != null) replacements[Replacements.KeyTablesInverse] = tableReplacements.Inverse(); SqlPreCommand syncEnums; try { syncEnums = SynchronizeEnumsScript(replacements); } catch(Exception e) { syncEnums = new SqlPreCommandSimple("-- Exception synchronizing enums: " + e.Message); } SqlPreCommand addForeingKeys = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlBuilder.AlterTableForeignKeys(tab), null, (tn, tab, dif) => Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, (cn, colModel) => colModel.ReferenceTable == null || colModel.AvoidForeignKey ? null : SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable), null, (cn, colModel, coldb) => { if (colModel.ReferenceTable == null || colModel.AvoidForeignKey) return null; if (coldb.ForeignKey == null || !colModel.ReferenceTable.Name.Equals(ChangeName(coldb.ForeignKey.TargetTable))) return SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable); var name = SqlBuilder.ForeignKeyName(tab.Name.Name, colModel.Name); return SqlPreCommand.Combine(Spacing.Simple, name != coldb.ForeignKey.Name.Name ? SqlBuilder.RenameForeignKey(coldb.ForeignKey.Name, name) : null, (coldb.ForeignKey.IsDisabled || coldb.ForeignKey.IsNotTrusted) && !replacements.SchemaOnly ? SqlBuilder.EnableForeignKey(tab.Name, name) : null); }, Spacing.Simple), Spacing.Double); bool? createMissingFreeIndexes = null; SqlPreCommand addIndices = Synchronizer.SynchronizeScript(model, database, (tn, tab) => modelIndices[tab].Values.Where(a => !(a is PrimaryClusteredIndex)).Select(SqlBuilder.CreateIndex).Combine(Spacing.Simple), null, (tn, tab, dif) => { var columnReplacements = replacements.TryGetC(Replacements.KeyColumnsForTable(tn)); Func<IColumn, bool> isNew = c => !dif.Columns.ContainsKey(columnReplacements?.TryGetC(c.Name) ?? c.Name); Dictionary<string, Index> modelIxs = modelIndices[tab]; var controlledIndexes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, (i, mix) => mix is UniqueIndex || mix.Columns.Any(isNew) || SafeConsole.Ask(ref createMissingFreeIndexes, "Create missing non-unique index {0} in {1}?".FormatWith(mix.IndexName, tab.Name)) ? SqlBuilder.CreateIndex(mix) : null, null, (i, mix, dix) => !dix.IndexEquals(dif, mix) ? SqlBuilder.CreateIndex(mix) : mix.IndexName != dix.IndexName ? SqlBuilder.RenameIndex(tab, dix.IndexName, mix.IndexName) : null, Spacing.Simple); return SqlPreCommand.Combine(Spacing.Simple, controlledIndexes); }, Spacing.Double); SqlPreCommand dropSchemas = Synchronizer.SynchronizeScriptReplacing(replacements, "Schemas", modelSchemas.ToDictionary(a => a.ToString()), databaseSchemas.ToDictionary(a => a.ToString()), null, (_, oldSN) => DropSchema(oldSN) ? SqlBuilder.DropSchema(oldSN) : null, (_, newSN, oldSN) => newSN.Equals(oldSN) ? null : SqlBuilder.DropSchema(oldSN), Spacing.Double); return SqlPreCommand.Combine(Spacing.Triple, createSchemas, dropStatistics, dropIndices, dropForeignKeys, tables, syncEnums, addForeingKeys, addIndices, dropSchemas); } }
public static void SynchronizeRoles(XDocument doc) { Table table = Schema.Current.Table(typeof(RoleEntity)); TableMList relationalTable = table.TablesMList().Single(); Dictionary <string, XElement> rolesXml = doc.Root.Element("Roles").Elements("Role").ToDictionary(x => x.Attribute("Name").Value); { Dictionary <string, RoleEntity> rolesDic = Database.Query <RoleEntity>().ToDictionary(a => a.ToString()); Replacements replacements = new Replacements(); replacements.AskForReplacements(rolesDic.Keys.ToHashSet(), rolesXml.Keys.ToHashSet(), "Roles"); rolesDic = replacements.ApplyReplacementsToOld(rolesDic, "Roles"); Console.WriteLine("Part 1: Syncronize roles without relationships"); var roleInsertsDeletes = Synchronizer.SynchronizeScript(Spacing.Double, rolesXml, rolesDic, createNew: (name, xelement) => table.InsertSqlSync(new RoleEntity { Name = name }, includeCollections: false), removeOld: (name, role) => SqlPreCommand.Combine(Spacing.Simple, new SqlPreCommandSimple("DELETE {0} WHERE {1} = {2} --{3}" .FormatWith(relationalTable.Name, ((IColumn)relationalTable.Field).Name.SqlEscape(), role.Id, role.Name)), table.DeleteSqlSync(role)), mergeBoth: (name, xElement, role) => { var oldName = role.Name; role.Name = name; role.MergeStrategy = xElement.Attribute("MergeStrategy")?.Let(t => t.Value.ToEnum <MergeStrategy>()) ?? MergeStrategy.Union; return(table.UpdateSqlSync(role, includeCollections: false, comment: oldName)); }); if (roleInsertsDeletes != null) { SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN ROLE SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), roleInsertsDeletes, new SqlPreCommandSimple("-- END ROLE SYNC SCRIPT")).OpenSqlFileRetry(); Console.WriteLine("Press [Enter] when executed..."); Console.ReadLine(); } else { SafeConsole.WriteLineColor(ConsoleColor.Green, "Already syncronized"); } } { Console.WriteLine("Part 2: Syncronize roles relationships"); Dictionary <string, RoleEntity> rolesDic = Database.Query <RoleEntity>().ToDictionary(a => a.ToString()); var roleRelationships = Synchronizer.SynchronizeScript(Spacing.Double, rolesXml, rolesDic, createNew: (name, xelement) => { throw new InvalidOperationException("No new roles should be at this stage. Did you execute the script?"); }, removeOld: (name, role) => { throw new InvalidOperationException("No old roles should be at this stage. Did you execute the script?"); }, mergeBoth: (name, xElement, role) => { var should = xElement.Attribute("Contains").Value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); var current = role.Roles.Select(a => a.ToString()); if (should.OrderBy().SequenceEqual(current.OrderBy())) { return(null); } role.Roles = should.Select(rs => rolesDic.GetOrThrow(rs).ToLite()).ToMList(); return(table.UpdateSqlSync(role)); }); if (roleRelationships != null) { SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN ROLE SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), roleRelationships, new SqlPreCommandSimple("-- END ROLE SYNC SCRIPT")).OpenSqlFileRetry(); Console.WriteLine("Press [Enter] when executed..."); Console.ReadLine(); } else { SafeConsole.WriteLineColor(ConsoleColor.Green, "Already syncronized"); } } }
public static SqlPreCommand SynchronizeTablesScript(Replacements replacements) { //Temproal HACK if (Database.View<SysIndexes>().Any(a => a.name.StartsWith("FIX")) && SafeConsole.Ask("Old index naming convention...rename first?")) { return Schema.Current.DatabaseNames().Select(db => { using (Administrator.OverrideDatabaseInViews(db)) { var indexes = (from s in Database.View<SysSchemas>() from t in s.Tables() from ix in t.Indices() where !ix.is_primary_key select new { schemaName = s.name, tableName = t.name, ix.is_unique, indexName = ix.name }).ToList(); return (from ix in indexes let newName = ix.is_unique ? Regex.Replace(ix.indexName, @"^IX_\w+?_", "UIX_") : Regex.Replace(ix.indexName, @"^F?IX_\w+?_", "IX_") where ix.indexName != newName select new SqlPreCommandSimple("EXEC SP_RENAME '{0}.{1}' , '{2}', 'INDEX' ".Formato( new ObjectName(new SchemaName(db, ix.schemaName), ix.tableName), ix.indexName, newName))).Combine(Spacing.Simple); } }).Combine(Spacing.Double); } Dictionary<string, ITable> model = Schema.Current.GetDatabaseTables().ToDictionary(a => a.Name.ToString(), "schema tables"); Dictionary<string, DiffTable> database = DefaultGetDatabaseDescription(Schema.Current.DatabaseNames()); replacements.AskForReplacements(database.Keys.ToHashSet(), model.Keys.ToHashSet(), Replacements.KeyTables); database = replacements.ApplyReplacementsToOld(database, Replacements.KeyTables); Dictionary<ITable, Dictionary<string, Index>> modelIndices = model.Values .ToDictionary(t => t, t => t.GeneratAllIndexes().ToDictionary(a => a.IndexName, "Indexes for {0}".Formato(t.Name))); model.JoinDictionaryForeach(database, (tn, tab, diff) => { var key = Replacements.KeyColumnsForTable(tn); replacements.AskForReplacements(diff.Colums.Keys.ToHashSet(), tab.Columns.Keys.ToHashSet(), key); diff.Colums = replacements.ApplyReplacementsToOld(diff.Colums, key); diff.Indices = ApplyIndexAutoReplacements(diff, tab, modelIndices[tab]); }); Func<ObjectName, ObjectName> ChangeName = (ObjectName objectName) => { string name = replacements.Apply(Replacements.KeyTables, objectName.ToString()); return model.TryGetC(name).Try(a => a.Name) ?? objectName; }; Func<DiffTable, DiffIndex, Index, bool> columnsChanged = (dif, dix, mix) => { if (dix.Columns.Count != mix.Columns.Length) return true; var dixColumns = dif.Colums.Where(kvp => dix.Columns.Contains(kvp.Value.Name)); return !dixColumns.All(kvp => dif.Colums.GetOrThrow(kvp.Key).ColumnEquals(mix.Columns.SingleEx(c => c.Name == kvp.Key))); }; //use database without replacements to just remove indexes SqlPreCommand dropStatistics = Synchronizer.SynchronizeScript(model, database, null, (tn, dif) => SqlBuilder.DropStatistics(tn, dif.Stats), (tn, tab, dif) => { var removedColums = dif.Colums.Keys.Except(tab.Columns.Keys).ToHashSet(); return SqlBuilder.DropStatistics(tn, dif.Stats.Where(a => a.Columns.Any(removedColums.Contains)).ToList()); }, Spacing.Double); SqlPreCommand dropIndices = Synchronizer.SynchronizeScript(model, database, null, (tn, dif) => dif.Indices.Values.Select(ix => SqlBuilder.DropIndex(dif.Name, ix)).Combine(Spacing.Simple), (tn, tab, dif) => { Dictionary<string, Index> modelIxs = modelIndices[tab]; var removedColums = dif.Colums.Keys.Except(tab.Columns.Keys).ToHashSet(); var changes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, null, (i, dix) => dix.IsControlledIndex || dix.Columns.Any(removedColums.Contains) ? SqlBuilder.DropIndex(dif.Name, dix) : null, (i, mix, dix) => (mix as UniqueIndex).Try(u => u.ViewName) != dix.ViewName || columnsChanged(dif, dix, mix) ? SqlBuilder.DropIndex(dif.Name, dix) : null, Spacing.Simple); return changes; }, Spacing.Double); SqlPreCommand dropForeignKeys = Synchronizer.SynchronizeScript( model, database, null, (tn, dif) => dif.Colums.Values.Select(c => c.ForeingKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, c.ForeingKey.Name) : null).Combine(Spacing.Simple), (tn, tab, dif) => Synchronizer.SynchronizeScript( tab.Columns, dif.Colums, null, (cn, colDb) => colDb.ForeingKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeingKey.Name) : null, (cn, colModel, colDb) => colDb.ForeingKey == null ? null : colModel.ReferenceTable == null || !colModel.ReferenceTable.Name.Equals(ChangeName(colDb.ForeingKey.TargetTable)) ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeingKey.Name) : null, Spacing.Simple), Spacing.Double); SqlPreCommand tables = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlBuilder.CreateTableSql(tab), (tn, dif) => SqlBuilder.DropTable(dif.Name), (tn, tab, dif) => SqlPreCommand.Combine(Spacing.Simple, !object.Equals(dif.Name, tab.Name) ? SqlBuilder.RenameOrMove(dif, tab) : null, Synchronizer.SynchronizeScript( tab.Columns, dif.Colums, (cn, tabCol) => SqlBuilder.AlterTableAddColumn(tab, tabCol), (cn, difCol) => SqlPreCommand.Combine(Spacing.Simple, difCol.DefaultConstraintName.HasText() ? SqlBuilder.AlterTableDropConstraint(tab.Name, difCol.DefaultConstraintName) : null, SqlBuilder.AlterTableDropColumn(tab, cn)), (cn, tabCol, difCol) =>SqlPreCommand.Combine(Spacing.Simple, difCol.Name == tabCol.Name ? null : SqlBuilder.RenameColumn(tab, difCol.Name, tabCol.Name), difCol.ColumnEquals(tabCol) ? null : SqlBuilder.AlterTableAlterColumn(tab, tabCol), UpdateByFkChange(tn, difCol, tabCol, ChangeName)), Spacing.Simple)), Spacing.Double); var tableReplacements = replacements.TryGetC(Replacements.KeyTables); if (tableReplacements != null) replacements[Replacements.KeyTablesInverse] = tableReplacements.Inverse(); SqlPreCommand syncEnums = SynchronizeEnumsScript(replacements); SqlPreCommand addForeingKeys = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlBuilder.AlterTableForeignKeys(tab), null, (tn, tab, dif) => Synchronizer.SynchronizeScript( tab.Columns, dif.Colums, (cn, colModel) => colModel.ReferenceTable == null ? null: SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable), null, (cn, colModel, coldb) => { if(colModel.ReferenceTable == null) return null; if(coldb.ForeingKey == null || !colModel.ReferenceTable.Name.Equals(ChangeName(coldb.ForeingKey.TargetTable))) return SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable); var name = SqlBuilder.ForeignKeyName(tab.Name.Name, colModel.Name); return SqlPreCommand.Combine(Spacing.Simple, name != coldb.ForeingKey.Name? SqlBuilder.RenameForeignKey(tab.Name.Schema, coldb.ForeingKey.Name, name) : null, (coldb.ForeingKey.IsDisabled || coldb.ForeingKey.IsNotTrusted) && !ExecutionMode.IsSynchronizeSchemaOnly ? SqlBuilder.EnableForeignKey(tab.Name, name) : null); }, Spacing.Simple), Spacing.Double); bool? createMissingFreeIndexes = null; SqlPreCommand addIndices = Synchronizer.SynchronizeScript(model, database, (tn, tab) => modelIndices[tab].Values.Select(SqlBuilder.CreateIndex).Combine(Spacing.Simple), null, (tn, tab, dif) => { var columnReplacements = replacements.TryGetC(Replacements.KeyColumnsForTable(tn)); Func<IColumn, bool> isNew = c => !dif.Colums.ContainsKey(columnReplacements.TryGetC(c.Name) ?? c.Name); Dictionary<string, Index> modelIxs = modelIndices[tab]; var controlledIndexes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, (i, mix) => mix is UniqueIndex || mix.Columns.Any(isNew) || SafeConsole.Ask(ref createMissingFreeIndexes, "Create missing non-unique index too?") ? SqlBuilder.CreateIndex(mix) : null, null, (i, mix, dix) => (mix as UniqueIndex).Try(u => u.ViewName) != dix.ViewName || columnsChanged(dif, dix, mix) ? SqlBuilder.CreateIndex(mix) : mix.IndexName != dix.IndexName ? SqlBuilder.RenameIndex(tab, dix.IndexName, mix.IndexName) : null, Spacing.Simple); return SqlPreCommand.Combine(Spacing.Simple, controlledIndexes); }, Spacing.Double); return SqlPreCommand.Combine(Spacing.Triple, dropStatistics, dropIndices, dropForeignKeys, tables, syncEnums, addForeingKeys, addIndices); }