public void GetPrimaryKey(DatabaseDefinition dd) { foreach (var table in dd.GetTables()) { GetPrimaryKey(table); } }
public void AddTableDocumentation(DatabaseDefinition dd) { var reader = Executer.ExecuteQuery(@" SELECT SCHEMA_NAME(t.schema_id) as SchemaName, t.name AS TableName, p.value AS Property FROM sys.tables AS t INNER JOIN sys.extended_properties AS p ON p.major_id = t.object_id AND p.minor_id = 0 AND p.class = 1 AND p.name = 'MS_Description'"); var tables = dd.GetTables(); foreach (var row in reader.Rows) { // TODO SchemaAndTableName.Schema might be null on default schema? var table = tables.Find(t => t.SchemaAndTableName.Schema == row.GetAs <string>("SchemaName") && t.SchemaAndTableName.TableName == row.GetAs <string>("TableName")); if (table != null) { var description = row.GetAs <string>("Property"); if (!string.IsNullOrEmpty(description)) { description = description.Replace("\\n", "\n", StringComparison.OrdinalIgnoreCase).Trim(); var descriptionProperty = new SqlTableDescription(table, description); table.Properties.Add(descriptionProperty); } } } }
public void GetIdentity(DatabaseDefinition dd) { foreach (var table in dd.GetTables()) { GetIdentity(table); } }
public void GetForeignKeys(DatabaseDefinition dd) { foreach (var table in dd.GetTables()) { GetForeignKeys(table); } }
public void Generate(DatabaseDefinition databaseDefinition) { Log(LogSeverity.Information, "Starting on {DatabaseName}.", "BimGenerator", DatabaseName); var root = new BimGeneratorRoot { Model = new BimGeneratorModel() }; BimHelper.SetDefaultAnnotations(root.Model); BimHelper.SetDefaultDataSources(root.Model, DatabaseName); var relationShipRegistrations = new RelationShipRegistrations(); foreach (var sqlTable in databaseDefinition.GetTables()) { if (!Context.Customizer.ShouldSkip(sqlTable.SchemaAndTableName) && !DocumenterWriterBase.ShouldSkipKnownTechnicalTable(sqlTable.SchemaAndTableName) ) { root.Model.Tables.Add(GenerateTable(sqlTable)); Context.Logger.Log(LogSeverity.Debug, "Table {TableName} added to bim model.", "BimGenerator", sqlTable.SchemaAndTableName); GatherReferencedTablesByFK(relationShipRegistrations, sqlTable); } } GenerateReferences(relationShipRegistrations, root.Model); var jsonString = ToJson(root); jsonString = RemoveInvalidEmptyItems(jsonString); WriteJson(jsonString); }
public void GetIndexes(DatabaseDefinition dd) { foreach (var table in dd.GetTables()) { GetIndex(table); } }
public void GenerateSingleFile(DatabaseDefinition databaseDefinition, string fileName, bool partialClass = false) { Log(LogSeverity.Information, "Starting on {DatabaseName}.", "CsGenerator", DatabaseName); var sb = new StringBuilder(); var tables = databaseDefinition .GetTables() .Where(x => Context.Customizer?.ShouldSkip(x.SchemaAndTableName) != true) .OrderBy(x => x.SchemaAndTableName.Schema) .ThenBy(x => x.SchemaAndTableName.TableName).ToList(); WriteSingleFileHeader(sb, tables, partialClass); var index = 0; foreach (var table in tables) { sb.AppendLine(); GenerateTable(sb, table); index++; } WriteSingleFileFooter(sb); Context.Logger.Log(LogSeverity.Information, "Writing Document file {FileName}", "Documenter", fileName); File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); }
public void GetForeignKeysAndUniqueConstrainsts(DatabaseDefinition dd) { foreach (var table in dd.GetTables()) { GetForeignKeys(table); GetUniqueConstraints(table); } }
public List <IMigration> Compare(DatabaseDefinition originalDd, DatabaseDefinition newDd) { // TODO needs to be ordered var changes = new List <IMigration>(); // Compare tables // handle renamed tables - needs parameter / external info foreach (var tableOriginal in originalDd.GetTables()) { if (!newDd.Contains(tableOriginal.SchemaAndTableName)) { var tableDelete = new TableDelete { SchemaAndTableName = tableOriginal.SchemaAndTableName }; changes.Add(tableDelete); } } foreach (var tableNewDd in newDd.GetTables()) { if (!originalDd.Contains(tableNewDd.SchemaAndTableName)) { var tableNew = new TableNew(tableNewDd); changes.Add(tableNew); } } foreach (var tableOriginal in originalDd.GetTables()) { // not deleted if (newDd.Contains(tableOriginal.SchemaAndTableName)) { var tableNew = newDd.Tables[tableOriginal.SchemaAndTableName]; changes.AddRange(CompareColumns(tableOriginal, tableNew)); changes.AddRange(ComparerPrimaryKey.ComparePrimaryKeys(tableOriginal, tableNew)); changes.AddRange(ComparerForeignKey.CompareForeignKeys(tableOriginal, tableNew)); changes.AddRange(ComparerIndex.CompareIndexes(tableOriginal, tableNew)); changes.AddRange(ComparerUniqueConstraint.CompareUniqueConstraints(tableOriginal, tableNew)); } } return(changes); }
public void GetIndexes(DatabaseDefinition dd) { foreach (var table in dd.GetTables()) { GetPrimaryKey(table); GetUniqueConstraints(table); GetIndexes(table); } }
public void GenerateMultiFile(DatabaseDefinition databaseDefinition) { Log(LogSeverity.Information, "Starting on {DatabaseName}.", "CsGenerator", DatabaseName); var sb = new StringBuilder(); WritePartialMainClassHeader(sb); sb.AppendLine("}"); var folder = Path.Combine(Context.GeneratorSettings.WorkingDirectory ?? @".\", DatabaseName); Directory.CreateDirectory(folder); File.WriteAllText(Path.Combine(folder, DatabaseName + ".cs"), sb.ToString(), Encoding.UTF8); var sqlTablesByCategory = new List <KeyValuePair <string, SqlTable> >(); foreach (var table in databaseDefinition.GetTables()) { if (!Context.Customizer.ShouldSkip(table.SchemaAndTableName)) { sqlTablesByCategory.Add(new KeyValuePair <string, SqlTable>(Context.Customizer.Category(table.SchemaAndTableName), table)); } } var tables = sqlTablesByCategory .OrderBy(kvp => kvp.Key) .ThenBy(t => t.Value.SchemaAndTableName.SchemaAndName); foreach (var tableKvp in tables) { var category = tableKvp.Key; var table = tableKvp.Value; sb.Clear(); WritePartialTableFileHeader(sb); GenerateTable(sb, table); WritePartialTableFileFooter(sb); var categoryInPath = category; if (string.IsNullOrEmpty(categoryInPath)) { categoryInPath = "_no_category_"; } categoryInPath = categoryInPath.Replace('?', '?'); folder = Path.Combine(Context.GeneratorSettings.WorkingDirectory ?? @".\", DatabaseName, categoryInPath); var fileName = Helper.GetSimplifiedSchemaAndTableName(table.SchemaAndTableName, ".") + ".cs"; Context.Logger.Log(LogSeverity.Information, "Writing Document file {FileName} to folder {Folder}", "Documenter", fileName, folder); Directory.CreateDirectory(folder); File.WriteAllText(Path.Combine(folder, fileName), sb.ToString(), Encoding.UTF8); } }
public List <SchemaCheck> Check(DatabaseDefinition dd) { var schemaChecks = new List <SchemaCheck>(); foreach (var table in dd.GetTables()) { schemaChecks.AddRange(CheckSelfReferencingFk(table)); schemaChecks.AddRange(CheckFkAndPkAreTheSame(table)); schemaChecks.AddRange(CheckFkContainsIdentity(table)); schemaChecks.AddRange(CheckTableSingularNameConvention(table)); } return(schemaChecks); }
//private readonly List<KeyValuePair<string, SqlTable>> _sqlTablesByCategoryNew = new List<KeyValuePair<string, SqlTable>>(); //private readonly List<KeyValuePair<string, SqlTable>> _skippedSqlTablesByCategoryNew = new List<KeyValuePair<string, SqlTable>>(); public void Document(DatabaseDefinition originalDd, DatabaseDefinition newDd) { Log(LogSeverity.Information, "Starting on {OriginalDatabaseName} vs. {NewDatabaseName}", "ChangeDocumenter", OriginalDatabaseName, NewDatabaseName); var tablesOriginal = RemoveKnownTechnicalTables(originalDd.GetTables()); foreach (var table in tablesOriginal) { if (!Context.CustomizerOriginal.ShouldSkip(table.SchemaAndTableName)) { _sqlTablesByCategoryOrignal.Add(new KeyValuePair <string, SqlTable>(Context.CustomizerOriginal.Category(table.SchemaAndTableName), table)); } else { _skippedSqlTablesByCategoryOriginal.Add(new KeyValuePair <string, SqlTable>(Context.CustomizerOriginal.Category(table.SchemaAndTableName), table)); } } var tablesNew = RemoveKnownTechnicalTables(newDd.GetTables()); foreach (var table in tablesNew) { if (!Context.CustomizerNew.ShouldSkip(table.SchemaAndTableName)) { _sqlTablesByCategoryOrignal.Add(new KeyValuePair <string, SqlTable>(Context.CustomizerNew.Category(table.SchemaAndTableName), table)); } else { _skippedSqlTablesByCategoryOriginal.Add(new KeyValuePair <string, SqlTable>(Context.CustomizerNew.Category(table.SchemaAndTableName), table)); } } WriteLine("Database", "", "Original", "New"); WriteLine("Database", "Database name", OriginalDatabaseName, NewDatabaseName); var noOfTablesOriginal = originalDd.GetTables().Count; var noOfNotSkippedTablesOriginal = originalDd.GetTables().Count(t => !Context.CustomizerOriginal.ShouldSkip(t.SchemaAndTableName)); var noOfTablesNew = newDd.GetTables().Count; var noOfNotSkippedTablesNew = newDd.GetTables().Count(t => !Context.CustomizerNew.ShouldSkip(t.SchemaAndTableName)); WriteLine("Database", "Number of documented tables", noOfNotSkippedTablesOriginal, noOfNotSkippedTablesNew); WriteLine("Database", "Number of skipped tables", noOfTablesOriginal - noOfNotSkippedTablesOriginal, noOfTablesNew - noOfNotSkippedTablesNew); WriteLine("Database", "Number of tables", noOfTablesOriginal, noOfTablesNew); var comparer = new Comparer(Context); var changes = comparer.Compare(originalDd, newDd); WriteLine("Tables", "Schema", "Table Name", "Event"); foreach (var tableDelete in changes.OfType <TableDelete>()) { if (!Context.CustomizerOriginal.ShouldSkip(tableDelete.SchemaAndTableName)) { WriteLine("Tables", tableDelete.SchemaAndTableName.Schema, tableDelete.SchemaAndTableName.TableName, "Deleted"); } } foreach (var tableNew in changes.OfType <TableNew>()) { if (!Context.CustomizerNew.ShouldSkip(tableNew.SchemaAndTableName)) { WriteLine("Tables", tableNew.SchemaAndTableName.Schema, tableNew.SchemaAndTableName.TableName, "Added"); } } var processedTables = new List <SchemaAndTableName>(); foreach (var change in changes.OfType <ColumnMigration>()) { switch (change) { //"Column Name", "Data Type (DbTools)", "Data Type", "Column Length", "Column Scale", "Allow Nulls", "Primary Key", "Identity", "Default Value", "Description", "Foreign Key Name", "Referenced Table", "Link", "Referenced Column" case ColumnNew column: { if (Context.CustomizerNew.ShouldSkip(column.Table.SchemaAndTableName)) { continue; } ProcessColumnMigration(processedTables, column.SqlColumn, "New"); break; } case ColumnDelete column: { if (Context.CustomizerOriginal.ShouldSkip(column.Table.SchemaAndTableName)) { continue; } ProcessColumnMigration(processedTables, column.SqlColumn, "Delete"); break; } case ColumnChange column: { if (Context.CustomizerNew.ShouldSkip(column.NewNameAndType.Table.SchemaAndTableName)) { continue; } ProcessColumnMigration(processedTables, column.SqlColumn, "Original"); ProcessColumnMigration(processedTables, column.NewNameAndType, "Changed to"); break; } } } if (!Context.DocumenterSettings.NoForeignKeys) { var processedFKs = new List <SchemaAndTableName>(); foreach (var change in changes.OfType <ForeignKeyMigration>()) { ProcessTable(processedTables, change.ForeignKey.SqlTable); // Ensure table header switch (change) { case ForeignKeyNew fkNew: { if (Context.CustomizerNew.ShouldSkip(fkNew.ForeignKey.ReferredTable.SchemaAndTableName)) { continue; } ProcessForeignKey(processedFKs, fkNew.ForeignKey, "New"); break; } case ForeignKeyDelete fkDelete: { if (Context.CustomizerOriginal.ShouldSkip(fkDelete.ForeignKey.ReferredTable.SchemaAndTableName)) { continue; } ProcessForeignKey(processedFKs, fkDelete.ForeignKey, "Delete"); break; } case ForeignKeyChange fkChange: { if (Context.CustomizerNew.ShouldSkip(fkChange.NewForeignKey.ReferredTable.SchemaAndTableName)) { continue; } ProcessForeignKey(processedFKs, fkChange.ForeignKey, "Original"); ProcessForeignKey(processedFKs, fkChange.NewForeignKey, "Change to"); break; } } } } if (!Context.DocumenterSettings.NoIndexes) { var processedIndexes = new List <SchemaAndTableName>(); foreach (var change in changes.OfType <IndexMigration>()) { ProcessTable(processedTables, change.Index.SqlTable); // Ensure table header switch (change) { case IndexNew indexNew: { if (Context.CustomizerNew.ShouldSkip(indexNew.Index.SqlTable.SchemaAndTableName)) { continue; } ProcessIndex(processedIndexes, indexNew.Index, "New"); break; } case IndexDelete indexDelete: { if (Context.CustomizerOriginal.ShouldSkip(indexDelete.Index.SqlTable.SchemaAndTableName)) { continue; } ProcessIndex(processedIndexes, indexDelete.Index, "Delete"); break; } case IndexChange indexChange: { if (Context.CustomizerNew.ShouldSkip(indexChange.NewIndex.SqlTable.SchemaAndTableName)) { continue; } ProcessIndex(processedIndexes, indexChange.Index, "Original"); ProcessIndex(processedIndexes, indexChange.NewIndex, "Change to"); break; } } } } if (!Context.DocumenterSettings.NoUniqueConstraints) { var processedUniqueConsreaints = new List <SchemaAndTableName>(); foreach (var change in changes.OfType <UniqueConstraintMigration>()) { ProcessTable(processedTables, change.UniqueConstraint.SqlTable); // Ensure table header switch (change) { case UniqueConstraintNew ucNew: { if (Context.CustomizerNew.ShouldSkip(ucNew.UniqueConstraint.SqlTable.SchemaAndTableName)) { continue; } ProcessUniqueConstraint(processedUniqueConsreaints, ucNew.UniqueConstraint, "New"); break; } case UniqueConstraintDelete ucDelete: { if (Context.CustomizerOriginal.ShouldSkip(ucDelete.UniqueConstraint.SqlTable.SchemaAndTableName)) { continue; } ProcessUniqueConstraint(processedUniqueConsreaints, ucDelete.UniqueConstraint, "Delete"); break; } case UniqueConstraintChange ucChange: { if (Context.CustomizerNew.ShouldSkip(ucChange.NewUniqueConstraint.SqlTable.SchemaAndTableName)) { continue; } ProcessUniqueConstraint(processedUniqueConsreaints, ucChange.UniqueConstraint, "Original"); ProcessUniqueConstraint(processedUniqueConsreaints, ucChange.NewUniqueConstraint, "Change to"); break; } } } } Log(LogSeverity.Information, "Generating Document content.", "ChangeDocumenter"); var content = DocumenterWriter.GetContent(); var fileName = FileName ?? (OriginalDatabaseName == null && NewDatabaseName == null ? "DatabaseChanges.xlsx" : $"{OriginalDatabaseName}_vs_{NewDatabaseName}.xlsx"); var path = Context.DocumenterSettings?.WorkingDirectory; Log(LogSeverity.Information, "Writing Document file {FileName} to folder {Folder}", "ChangeDocumenter", fileName, path); if (!string.IsNullOrEmpty(path)) { fileName = Path.Combine(path, fileName); } File.WriteAllBytes(fileName, content); }
public static RelationalModel Convert(DatabaseDefinition sourceDefinition, string defaultSourceSchemaName, CaseInsensitiveStringKeyDictionary <string> schemaNameMap = null, Func <SqlTable, bool> filterDelegate = null) { var newDefaultSchemaName = schemaNameMap?[defaultSourceSchemaName] ?? defaultSourceSchemaName; var newModel = new RelationalModel(newDefaultSchemaName); var sourceTablesOrdered = sourceDefinition.GetTables() .Where(x => filterDelegate?.Invoke(x) != false) .OrderBy(x => x.SchemaAndTableName.SchemaAndName) .ToList(); foreach (var sourceTable in sourceTablesOrdered) { var newSchemaName = schemaNameMap?[sourceTable.SchemaAndTableName.Schema] ?? sourceTable.SchemaAndTableName.Schema ?? newDefaultSchemaName; var newSchema = newModel[newSchemaName] ?? newModel.AddSchema(newSchemaName); var primaryKey = sourceTable.Properties.OfType <PrimaryKey>().FirstOrDefault(); var newTable = newSchema.AddTable(sourceTable.SchemaAndTableName.TableName); foreach (var property in sourceTable.Properties.OfType <DwhTableFlagProperty>()) { newTable.SetFlag(property.Name, true); } foreach (var property in sourceTable.Properties.OfType <DwhTableDataProperty>()) { newTable.SetAdditionalData(property.Name, property.Value); } if (sourceTable.HasProperty <EtlRunInfoDisabledProperty>()) { newTable.SetEtlRunInfoDisabled(); } if (sourceTable.HasProperty <HasHistoryTableProperty>()) { newTable.SetHasHistoryTable(); } var sourceTableNameOverrideProperty = sourceTable.Properties.OfType <SourceTableNameOverrideProperty>().FirstOrDefault(); if (sourceTableNameOverrideProperty != null) { newTable.SetSourceTableNameOverride(sourceTableNameOverrideProperty.SourceTableName); } foreach (var sourceColumn in sourceTable.Columns) { var partOfPrimaryKey = primaryKey?.SqlColumns.Any(x => x.SqlColumn == sourceColumn) == true; var newColumn = newTable.AddColumn(sourceColumn.Name, partOfPrimaryKey); foreach (var property in sourceColumn.Properties.OfType <DwhColumnFlagProperty>()) { newColumn.SetFlag(property.Name, true); } foreach (var property in sourceColumn.Properties.OfType <DwhColumnDataProperty>()) { newColumn.SetAdditionalData(property.Name, property.Value); } if (sourceColumn.HasProperty <Identity>()) { newColumn.SetIdentity(); } if (sourceColumn.HasProperty <HistoryDisabledProperty>()) { newColumn.SetHistoryDisabled(); } if (sourceColumn.HasProperty <RecordTimestampIndicatorProperty>()) { newColumn.SetRecordTimestampIndicator(); } } } foreach (var table in sourceTablesOrdered) { var newSourceSchemaName = schemaNameMap?[table.SchemaAndTableName.Schema] ?? table.SchemaAndTableName.Schema ?? newDefaultSchemaName; var newSourceSchema = newModel[newSourceSchemaName]; var newSourceTable = newSourceSchema[table.SchemaAndTableName.TableName]; foreach (var fk in table.Properties.OfType <ForeignKey>()) { var newTargetSchemaName = schemaNameMap?[fk.ReferredTable.SchemaAndTableName.Schema] ?? fk.ReferredTable.SchemaAndTableName.Schema ?? newDefaultSchemaName; var newTargetSchema = newModel[newTargetSchemaName]; var newTargetTable = newTargetSchema[fk.ReferredTable.SchemaAndTableName.TableName]; if (newTargetTable == null) // target table is filtered out { continue; } var newFk = newSourceTable.AddForeignKeyTo(newTargetTable); foreach (var map in fk.ForeignKeyColumns) { var sourceColumn = newSourceTable[map.ForeignKeyColumn.Name]; var targetColumn = newTargetTable[map.ReferredColumn.Name]; newFk.AddColumnPair(sourceColumn, targetColumn); } } } return(newModel); }
public void Document(DatabaseDefinition databaseDefinition) { Log(LogSeverity.Information, "Starting on {DatabaseName}.", "Documenter", DatabaseName); var tables = RemoveKnownTechnicalTables(databaseDefinition.GetTables()); foreach (var table in tables) { if (!Customizer.ShouldSkip(table.SchemaAndTableName)) { _sqlTablesByCategory.Add(new KeyValuePair <string, SqlTable>(Customizer.Category(table.SchemaAndTableName), table)); } else { _skippedSqlTablesByCategory.Add(new KeyValuePair <string, SqlTable>(Customizer.Category(table.SchemaAndTableName), table)); } } var hasCategories = _sqlTablesByCategory.Any(x => !string.IsNullOrEmpty(x.Key)); var noOfTables = databaseDefinition.GetTables().Count; var noOfNotSkippedTables = databaseDefinition.GetTables().Count(t => !Customizer.ShouldSkip(t.SchemaAndTableName)); WriteLine("Database", "Database name", DatabaseName); WriteLine("Database", "Number of documented tables", noOfNotSkippedTables); WriteLine("Database", "Number of skipped tables", noOfTables - noOfNotSkippedTables); WriteLine("Database", "Number of tables", noOfTables); if (hasCategories) { WriteLine("Database"); WriteLine("Database", "Documented category", "Table count"); Context.Logger.Log(LogSeverity.Verbose, "Writing tables by category.", "Documenter"); foreach (var category in _sqlTablesByCategory.Select(kvp => kvp.Key).Distinct().OrderBy(x => x)) { WriteLine("Database", category ?? "(No category)", _sqlTablesByCategory.Count(kvp => kvp.Key == category)); } if (_skippedSqlTablesByCategory.Count > 0) { WriteLine("Database"); WriteLine("Database", "Skipped category", "Table count"); foreach (var category in _skippedSqlTablesByCategory.Select(kvp => kvp.Key).Distinct().OrderBy(x => x)) { WriteLine("Database", category ?? "(No category)", _skippedSqlTablesByCategory.Count(kvp => kvp.Key == category)); } } WriteLine("Tables", "Category", "Schema", "Table Name", "Link", "Number of columns", "Description"); if (!Context.DocumenterSettings.NoInternalDataTypes) { WriteLine("All columns", "Category", "Schema", "Table Name", "Column Name", "Data Type (DbTools)", "Data Type", "Column Length", "Column Scale", "Allow Nulls", "Primary Key", "Identity", "Default Value", "Description"); } else { WriteLine("All columns", "Category", "Schema", "Table Name", "Column Name", "Data Type", "Column Length", "Column Scale", "Allow Nulls", "Primary Key", "Identity", "Default Value", "Description"); } } else { WriteLine("Tables", "Schema", "Table Name", "Link", "Number of columns", "Description"); if (!Context.DocumenterSettings.NoInternalDataTypes) { WriteLine("All columns", "Schema", "Table Name", "Column Name", "Data Type (DbTools)", "Data Type", "Column Length", "Column Scale", "Allow Nulls", "Primary Key", "Identity", "Default Value", "Description"); } else { WriteLine("All columns", "Schema", "Table Name", "Column Name", "Data Type", "Column Length", "Column Scale", "Allow Nulls", "Primary Key", "Identity", "Default Value", "Description"); } } // Ensure sheet order if (Customizer is PatternMatchingTableCustomizer) { Write("Patt.ma.-tables"); Write("Patt.ma.-patterns"); Write("Patt.ma.-ma.s w exceptions"); Write("Patt.ma.-no matches (unused)"); } foreach (var tableKvp in _sqlTablesByCategory.OrderBy(kvp => kvp.Key).ThenBy(t => t.Value.SchemaAndTableName.Schema).ThenBy(t => t.Value.SchemaAndTableName.TableName)) { Context.Logger.Log(LogSeverity.Verbose, "Generating {TableName}.", "Documenter", tableKvp.Value.SchemaAndTableName); var category = tableKvp.Key; var table = tableKvp.Value; AddTableToTableList(category, table, hasCategories); var sheetColor = GetColor(table.SchemaAndTableName); if (sheetColor != null) { DocumenterWriter.SetSheetColor(Helper.GetSimplifiedSchemaAndTableName(table.SchemaAndTableName), sheetColor.Value); } AddTableHeader(hasCategories, category, table); AddTableDetails(category, table, hasCategories); } WriteLine("Tables"); foreach (var tableKvp in _skippedSqlTablesByCategory.OrderBy(kvp => kvp.Key).ThenBy(t => t.Value.SchemaAndTableName.Schema).ThenBy(t => t.Value.SchemaAndTableName.TableName)) { var category = tableKvp.Key; var table = tableKvp.Value; AddTableToTableList(category, table, hasCategories); } Context.Logger.Log(LogSeverity.Verbose, "Generating pattern matching info.", "Documenter"); AddPatternMatching(); AddPatternMatchingNoMatch(); Log(LogSeverity.Information, "Generating Document content.", "Documenter"); var content = DocumenterWriter.GetContent(); var fileName = FileName ?? (DatabaseName?.Length == 0 ? "Database.xlsx" : DatabaseName + ".xlsx"); var path = Context.DocumenterSettings?.WorkingDirectory; Log(LogSeverity.Information, "Writing Document file {FileName} to folder {Folder}", "Documenter", fileName, path); if (!string.IsNullOrEmpty(path)) { fileName = Path.Combine(path, fileName); } File.WriteAllBytes(fileName, content); }