From afd5c85c0f797113d2e9a96856e5d91e81a4bf42 Mon Sep 17 00:00:00 2001 From: Tomas Bouda Date: Thu, 25 Jan 2024 14:34:59 +0100 Subject: [PATCH] Added CLI app --- src/DbDiagramIo.CLI/DbDiagramIo.CLI.csproj | 20 +++ src/DbDiagramIo.CLI/Program.cs | 91 +++++++++++++ src/DbDiagramIo.CLI/flow-diagram.ico | Bin 0 -> 4286 bytes src/DbDiagramIo.Core/DbDiagramIo.Core.csproj | 11 ++ src/DbDiagramIo.Core/EnumerableExtensions.cs | 14 ++ .../Extensions/StringExtensions.cs | 16 +++ src/DbDiagramIo.MsSql.sln | 35 +++-- src/DbDiagramIo.MsSql/ColumnDto.cs | 33 ----- .../DbDiagramIo.MsSql.csproj | 4 + src/DbDiagramIo.MsSql/DbmlSchema.cs | 23 ++++ src/DbDiagramIo.MsSql/ForeignKeyDto.cs | 23 ---- src/DbDiagramIo.MsSql/MsSqlSchemaReader.cs | 126 +++++++++--------- .../Objects/Base/IDbmlObject.cs | 11 ++ src/DbDiagramIo.MsSql/TableDto.cs | 37 ----- .../Example.DotNetCore.csproj | 4 + src/Example.DotNetCore/Program.cs | 42 +++--- src/Example.Dotnet461/App.config | 6 +- .../Example.Dotnet461.csproj | 3 +- src/Example.Dotnet461/Program.cs | 36 ++--- 19 files changed, 329 insertions(+), 206 deletions(-) create mode 100644 src/DbDiagramIo.CLI/DbDiagramIo.CLI.csproj create mode 100644 src/DbDiagramIo.CLI/Program.cs create mode 100644 src/DbDiagramIo.CLI/flow-diagram.ico create mode 100644 src/DbDiagramIo.Core/DbDiagramIo.Core.csproj create mode 100644 src/DbDiagramIo.Core/EnumerableExtensions.cs create mode 100644 src/DbDiagramIo.Core/Extensions/StringExtensions.cs delete mode 100644 src/DbDiagramIo.MsSql/ColumnDto.cs create mode 100644 src/DbDiagramIo.MsSql/DbmlSchema.cs delete mode 100644 src/DbDiagramIo.MsSql/ForeignKeyDto.cs create mode 100644 src/DbDiagramIo.MsSql/Objects/Base/IDbmlObject.cs delete mode 100644 src/DbDiagramIo.MsSql/TableDto.cs diff --git a/src/DbDiagramIo.CLI/DbDiagramIo.CLI.csproj b/src/DbDiagramIo.CLI/DbDiagramIo.CLI.csproj new file mode 100644 index 0000000..89f1bf6 --- /dev/null +++ b/src/DbDiagramIo.CLI/DbDiagramIo.CLI.csproj @@ -0,0 +1,20 @@ + + + + Exe + net6.0 + enable + enable + flow-diagram.ico + + + + + + + + + + + + diff --git a/src/DbDiagramIo.CLI/Program.cs b/src/DbDiagramIo.CLI/Program.cs new file mode 100644 index 0000000..f33e4b8 --- /dev/null +++ b/src/DbDiagramIo.CLI/Program.cs @@ -0,0 +1,91 @@ +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using CommandLine; +using DbDiagramIo.Core; +using DbDiagramIo.Core.Extensions; +using DbDiagramIo.MsSql; +using Serilog; +using Serilog.Events; + +Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .CreateLogger(); + +Log.Information("DbDiagramIo.CLI v{Version}", Assembly.GetExecutingAssembly().GetName().Version); + +Parser.Default.ParseArguments(args) + .WithParsed(o => + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Is(o.LogEventLevel) + .WriteTo.Console() + .CreateLogger(); + + if (!string.IsNullOrEmpty(o.ConnectionString)) + { + var schemaReader = new MsSqlSchemaReader(o.ConnectionString); + var schema = schemaReader.GetDbmlSchema(); + var dbml = new StringBuilder(); + + var regexOptions = RegexOptions.IgnoreCase; + var excludedSchemas = o.ExcludedSchemas?.Where(s => !string.IsNullOrEmpty(s)).ToList(); + var excludedTables = o.ExcludedTables?.Where(s => !string.IsNullOrEmpty(s)).ToList(); + + Log.Debug("{Count} tables in total", schema.Tables.Count); + + schema.Tables + .Where(t => excludedSchemas.AllSafe(s => !t.Schema.Like(s, regexOptions), true) && excludedTables.AllSafe(s => !t.Name.Like(s, regexOptions), true)) + .ToList() + .ForEach(t => dbml.AppendLine(t.ToDbml())); + + Log.Debug("{Count} foreign keys in total", schema.ForeignKeys.Count); + + schema.ForeignKeys + .Where(f => o.ExcludedSchemas.AllSafe(s => !f.SourceTable.Schema.Like(s, regexOptions), true) && o.ExcludedTables.AllSafe(s => !f.SourceTable.Name.Like(s, regexOptions), true) + && o.ExcludedSchemas.AllSafe(s => !f.TargetTable.Schema.Like(s, regexOptions), true) && o.ExcludedTables.AllSafe(s => !f.TargetTable.Name.Like(s, regexOptions), true)) + .ToList() + .ForEach(t => dbml.AppendLine(t.ToDbml())); + + if (!string.IsNullOrEmpty(o.OutputFile)) + { + var dirName = Path.GetDirectoryName(o.OutputFile); + if (!string.IsNullOrEmpty(dirName) && !Directory.Exists(dirName)) + { + Directory.CreateDirectory(dirName); + } + + + File.WriteAllText(o.OutputFile, dbml.ToString()); + + Log.Information("Dbml file created at {OutputFile}", o.OutputFile); + } + else + { + Log.Information("Dbml output:"); + Console.WriteLine(dbml.ToString()); + } + } + else + { + Log.Error("No connection string provided!"); + } + }); + +public class Options +{ + [Option('v', "verbosity", Required = false, HelpText = "Sets the serilog log level.", Default = LogEventLevel.Information)] + public LogEventLevel LogEventLevel { get; set; } + + [Option('c', "connection-string", Required = true, HelpText = "The connection string to the database.")] + public string ConnectionString { get; set; } = null!; + + [Option("exclude-schemas", Required = false, HelpText = "The schemas to exclude from the dbml output. You can use LIKE syntax here.", Separator = ';')] + public IEnumerable? ExcludedSchemas { get; set; } + + [Option("exclude-tables", Required = false, HelpText = "The schemas to exclude from the dbml output. You can use LIKE syntax here e.g. 'AspNet%' to exclude all tables starting with 'AspNet'.", Separator = ';')] + public IEnumerable? ExcludedTables { get; set; } + + [Option('o', "output-file", Required = false, HelpText = "The file to write the dbml output to. If not provided, the output will be written to the console.")] + public string? OutputFile { get; set; } +} \ No newline at end of file diff --git a/src/DbDiagramIo.CLI/flow-diagram.ico b/src/DbDiagramIo.CLI/flow-diagram.ico new file mode 100644 index 0000000000000000000000000000000000000000..2d4ec03c1964f80380ee72383be06892ed4c67e3 GIT binary patch literal 4286 zcmds)OKcNY6o$`?6Kuz-T@oIuRAoYO5)-6a*r=sQycQIaR6LqME3s&kMJa^^5|8W> z1L>xlQp5ta5~?a%%wt!n4PAv+ZRoB>0Tw{U4 z{t)mq!@$!t3=22pz;SRGR62*EzBOR$Zr`N(!DiKtw(|PWqAdcymP%gxu_4C018ZK| zp0SzVw7Ts2OG2}u{}#q#tdD|xspK{Nj|Z!&0vH#+BY#CpNdT2RyyZp3A z&2*v``;X@@GlL=2pUyu{JKMa@UkYu10hZNTC41R~*t$SCIu=>{_EexDHpXi}|BF~P z`R5e~>RaqLfxrZ+q^0(Du4x0jiKpZv4ltPhPZ?A)hsp8Q8sKPz}NP5Odr^6VX5*n2?D zIiai2{{pzZ4;+I3BJ*E``KPGAdB3egy=U~#L4RA#^cm7N#+PwU9J^%4{fYQ5uOR;Z z^G|XAVZSx+{aTaf@0Qztb(nWoiFz&qR&D+%p1(+JERt9;6+nqG-OgVV{GVGcMNt~X_5oh6g!wu=7rp=ftpCg%iX1iH(>(^g6Tar(OT38JyDDJ-{k zr9^-KNc6^T(ZBbI<~|WUxKeaML~h}peh_j7Lg_ZvjOHh+9`E0WaaT4zS0;|XD>}Zj zsC^y!g!3S$wumm7Clk2$3}7F^claFN={a^e^Z5R-^;;Wd;@eHo!kVe)rr$A7{)L94 z&pa{AZyA2maO^6Ko%n{;61B^-+)%M2fI(qgFxl@_f|FugS}(m6uE`KdsIB g=<6+#Um;SsAF~Rh0jnTlqoV=U`!Usm1=%(FA4jXiQ~&?~ literal 0 HcmV?d00001 diff --git a/src/DbDiagramIo.Core/DbDiagramIo.Core.csproj b/src/DbDiagramIo.Core/DbDiagramIo.Core.csproj new file mode 100644 index 0000000..fb41126 --- /dev/null +++ b/src/DbDiagramIo.Core/DbDiagramIo.Core.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/DbDiagramIo.Core/EnumerableExtensions.cs b/src/DbDiagramIo.Core/EnumerableExtensions.cs new file mode 100644 index 0000000..6fc6845 --- /dev/null +++ b/src/DbDiagramIo.Core/EnumerableExtensions.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DbDiagramIo.Core +{ + public static class EnumerableExtensions + { + public static bool AllSafe(this IEnumerable source, Func predicate, bool valueIfNull = false) + { + return source?.All(predicate) ?? valueIfNull; + } + } +} \ No newline at end of file diff --git a/src/DbDiagramIo.Core/Extensions/StringExtensions.cs b/src/DbDiagramIo.Core/Extensions/StringExtensions.cs new file mode 100644 index 0000000..2414599 --- /dev/null +++ b/src/DbDiagramIo.Core/Extensions/StringExtensions.cs @@ -0,0 +1,16 @@ +using System.Text.RegularExpressions; + +namespace DbDiagramIo.Core.Extensions +{ + public static class StringExtensions + { + public static bool Like(this string toSearch, string toFind, RegexOptions regexOptions = RegexOptions.None) + { + return new Regex(@"\A" + new Regex(@"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\") + .Replace(toFind, ch => @"\" + ch) + .Replace('_', '.') + .Replace("%", ".*") + @"\z", RegexOptions.Singleline | regexOptions) + .IsMatch(toSearch); + } + } +} \ No newline at end of file diff --git a/src/DbDiagramIo.MsSql.sln b/src/DbDiagramIo.MsSql.sln index 9d6ed1b..1523ed4 100644 --- a/src/DbDiagramIo.MsSql.sln +++ b/src/DbDiagramIo.MsSql.sln @@ -1,13 +1,23 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.136 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32328.378 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbDiagramIo.MsSql", "DbDiagramIo.MsSql\DbDiagramIo.MsSql.csproj", "{2B68DE74-9ECE-4A00-8263-0D3E4E608408}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbDiagramIo.MsSql", "DbDiagramIo.MsSql\DbDiagramIo.MsSql.csproj", "{2B68DE74-9ECE-4A00-8263-0D3E4E608408}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.DotNetCore", "Example.DotNetCore\Example.DotNetCore.csproj", "{AD3E870C-FD95-4601-8D2A-04B102369D87}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example.DotNetCore", "Example.DotNetCore\Example.DotNetCore.csproj", "{AD3E870C-FD95-4601-8D2A-04B102369D87}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.Dotnet461", "Example.Dotnet461\Example.Dotnet461.csproj", "{E417CD4E-4C05-4241-888A-C99D1198DABD}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{416AAB75-36B3-48B2-A43A-FFB7F3928FDC}" + ProjectSection(SolutionItems) = preProject + ..\.gitignore = ..\.gitignore + ..\README.md = ..\README.md + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbDiagramIo.CLI", "DbDiagramIo.CLI\DbDiagramIo.CLI.csproj", "{88EDD710-12DA-4C1F-9F2C-2415835A3402}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D401BF61-DB8D-4D60-A3A0-E01967458D32}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbDiagramIo.Core", "DbDiagramIo.Core\DbDiagramIo.Core.csproj", "{75DA610B-BBB1-42AB-A1B7-7BAB7AD57D3B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,10 +33,14 @@ Global {AD3E870C-FD95-4601-8D2A-04B102369D87}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD3E870C-FD95-4601-8D2A-04B102369D87}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD3E870C-FD95-4601-8D2A-04B102369D87}.Release|Any CPU.Build.0 = Release|Any CPU - {E417CD4E-4C05-4241-888A-C99D1198DABD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E417CD4E-4C05-4241-888A-C99D1198DABD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E417CD4E-4C05-4241-888A-C99D1198DABD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E417CD4E-4C05-4241-888A-C99D1198DABD}.Release|Any CPU.Build.0 = Release|Any CPU + {88EDD710-12DA-4C1F-9F2C-2415835A3402}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88EDD710-12DA-4C1F-9F2C-2415835A3402}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88EDD710-12DA-4C1F-9F2C-2415835A3402}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88EDD710-12DA-4C1F-9F2C-2415835A3402}.Release|Any CPU.Build.0 = Release|Any CPU + {75DA610B-BBB1-42AB-A1B7-7BAB7AD57D3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75DA610B-BBB1-42AB-A1B7-7BAB7AD57D3B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75DA610B-BBB1-42AB-A1B7-7BAB7AD57D3B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75DA610B-BBB1-42AB-A1B7-7BAB7AD57D3B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -34,4 +48,7 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {72353AAB-C945-4F24-9FBF-41DFABBC3CB2} EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AD3E870C-FD95-4601-8D2A-04B102369D87} = {D401BF61-DB8D-4D60-A3A0-E01967458D32} + EndGlobalSection EndGlobal diff --git a/src/DbDiagramIo.MsSql/ColumnDto.cs b/src/DbDiagramIo.MsSql/ColumnDto.cs deleted file mode 100644 index 3cd2d2a..0000000 --- a/src/DbDiagramIo.MsSql/ColumnDto.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace DbDbiagramIo.MsSql -{ - internal class ColumnDto - { - public string TableName; - public string Name; - public int OrdinalPosition; - public string SqlType; - - private string DbDiagramType - { - get - { - string sqlTypeLowerCase = this.SqlType.ToLowerInvariant(); - - switch (sqlTypeLowerCase) - { - case "nvarchar": - return "varchar"; - case "datetime2": - return "datetime"; - default: - return sqlTypeLowerCase; - } - } - } - - public string ToDbDiagramCode() - { - return $"{this.Name} {DbDiagramType}"; - } - } -} diff --git a/src/DbDiagramIo.MsSql/DbDiagramIo.MsSql.csproj b/src/DbDiagramIo.MsSql/DbDiagramIo.MsSql.csproj index 809bdf3..b3a1128 100644 --- a/src/DbDiagramIo.MsSql/DbDiagramIo.MsSql.csproj +++ b/src/DbDiagramIo.MsSql/DbDiagramIo.MsSql.csproj @@ -8,4 +8,8 @@ + + + + diff --git a/src/DbDiagramIo.MsSql/DbmlSchema.cs b/src/DbDiagramIo.MsSql/DbmlSchema.cs new file mode 100644 index 0000000..ca9ee6d --- /dev/null +++ b/src/DbDiagramIo.MsSql/DbmlSchema.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using DbDiagramIo.MsSql.Objects; +using DbDiagramIo.MsSql.Objects.Base; + +namespace DbDiagramIo.MsSql +{ + public class DbmlSchema : IDbmlObject + { + public HashSet Tables { get; } + public HashSet ForeignKeys { get; } + + public DbmlSchema(HashSet
tables, HashSet foreignKeys) + { + Tables = tables; + ForeignKeys = foreignKeys; + } + + public string ToDbml() + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/DbDiagramIo.MsSql/ForeignKeyDto.cs b/src/DbDiagramIo.MsSql/ForeignKeyDto.cs deleted file mode 100644 index 5632693..0000000 --- a/src/DbDiagramIo.MsSql/ForeignKeyDto.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace DbDbiagramIo.MsSql -{ - public class ForeignKeyDto - { - public string SourceTableName { get; } - public string SourceColumnName { get; } - public string DestinationTableName { get; } - public string DestinationColumnName { get; } - - public ForeignKeyDto( string sourceTableName, string sourceColumnName, string destinationTableName, string destinationColumnName ) - { - SourceTableName = sourceTableName; - SourceColumnName = sourceColumnName; - DestinationTableName = destinationTableName; - DestinationColumnName = destinationColumnName; - } - - public string ToDbDiagramDto() - { - return $"Ref: {SourceTableName}.{SourceColumnName} > {DestinationTableName}.{DestinationColumnName}"; - } - } -} diff --git a/src/DbDiagramIo.MsSql/MsSqlSchemaReader.cs b/src/DbDiagramIo.MsSql/MsSqlSchemaReader.cs index 0d3762e..d8992a4 100644 --- a/src/DbDiagramIo.MsSql/MsSqlSchemaReader.cs +++ b/src/DbDiagramIo.MsSql/MsSqlSchemaReader.cs @@ -1,79 +1,79 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Data.SqlClient; using System.Globalization; +using System.Linq; +using DbDiagramIo.MsSql.Objects; -namespace DbDbiagramIo.MsSql +namespace DbDiagramIo.MsSql { - public class MsSqlSchemaReader - { - private const string ForeignKeysQuery = "SELECT constraints.CONSTRAINT_NAME as constraint_name, column_source.TABLE_NAME as source_table, column_source.COLUMN_NAME as source_column, column_destination.TABLE_NAME as destination_table, column_destination.COLUMN_NAME as destination_column FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS constraints join INFORMATION_SCHEMA.KEY_COLUMN_USAGE column_source on constraints.CONSTRAINT_NAME = column_source.CONSTRAINT_NAME join INFORMATION_SCHEMA.KEY_COLUMN_USAGE column_destination on constraints.UNIQUE_CONSTRAINT_NAME = column_destination.CONSTRAINT_NAME and column_source.ORDINAL_POSITION = column_destination.ORDINAL_POSITION;"; - private const string ColumnsQuery = "select table_name, column_name, ordinal_position, DATA_TYPE from information_schema.COLUMNS;"; + public class MsSqlSchemaReader + { + private const string ForeignKeysQuery = "SELECT constraints.CONSTRAINT_NAME as constraint_name, column_source.TABLE_SCHEMA AS source_table_schema, column_source.TABLE_NAME as source_table, column_source.COLUMN_NAME as source_column, column_destination.TABLE_SCHEMA AS destination_table_schema, column_destination.TABLE_NAME as destination_table, column_destination.COLUMN_NAME as destination_column FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS constraints join INFORMATION_SCHEMA.KEY_COLUMN_USAGE column_source on constraints.CONSTRAINT_NAME = column_source.CONSTRAINT_NAME join INFORMATION_SCHEMA.KEY_COLUMN_USAGE column_destination on constraints.UNIQUE_CONSTRAINT_NAME = column_destination.CONSTRAINT_NAME and column_source.ORDINAL_POSITION = column_destination.ORDINAL_POSITION;"; + private const string ColumnsQuery = "select table_schema, table_name, column_name, ordinal_position, DATA_TYPE from information_schema.COLUMNS;"; - public static (TableDto[], ForeignKeyDto[]) ReadTablesAndForeignKeysFromDb( string connectionString ) - { - List columns = new List(); - List foreignKeys = new List(); + public string ConnectionString { get; } - using (SqlConnection connection = new SqlConnection( connectionString )) - { - using (SqlCommand cmd = new SqlCommand( ColumnsQuery, connection )) - { - connection.Open(); - using (SqlDataReader reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - string tableName = (string)reader[ "table_name" ]; - string name = (string)reader[ "column_name" ]; - int ordinalPosition = int.Parse( reader[ "ordinal_position" ].ToString(), CultureInfo.InvariantCulture ); - string sqlType = (string)reader[ "DATA_TYPE" ]; + public MsSqlSchemaReader(string connectionString) + { + ConnectionString = connectionString; + } - ColumnDto columnDto = new ColumnDto() - { - Name = name, - OrdinalPosition = ordinalPosition, - SqlType = sqlType, - TableName = tableName - }; + public DbmlSchema GetDbmlSchema() + { + var tables = new HashSet
(); + var foreignKeys = new HashSet(); - columns.Add( columnDto ); - } - } - } + using (SqlConnection connection = new SqlConnection(ConnectionString)) + { + using (SqlCommand cmd = new SqlCommand(ColumnsQuery, connection)) + { + connection.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + string tableSchema = (string)reader["table_schema"]; + string tableName = (string)reader["table_name"]; + string name = (string)reader["column_name"]; + int ordinalPosition = int.Parse(reader["ordinal_position"].ToString(), CultureInfo.InvariantCulture); + string sqlType = (string)reader["DATA_TYPE"]; - using (SqlCommand cmd = new SqlCommand( ForeignKeysQuery, connection )) - { - using (SqlDataReader reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - string sourceTableName = (string)reader[ "source_table" ]; - string sourceColumnName = (string)reader[ "source_column" ]; - string destinationTableName = (string)reader[ "destination_table" ]; - string destinationColumnName = (string)reader[ "destination_column" ]; + var table = tables.SingleOrDefault(t => t.Name == tableName && t.Schema == tableSchema); + if (table == null) + { + table = new Table(tableName, tableSchema); + tables.Add(table); + } - ForeignKeyDto fk = new ForeignKeyDto( sourceTableName, sourceColumnName, destinationTableName, destinationColumnName ); + var column = new Column(name, sqlType, ordinalPosition); + table.AddColumn(column); + } + } + } - foreignKeys.Add( fk ); - } - } - } + using (SqlCommand cmd = new SqlCommand(ForeignKeysQuery, connection)) + { + using (SqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var sourceTable = new Table((string)reader["source_table"], (string)reader["source_table_schema"]); + var destinationTable = new Table((string)reader["destination_table"], (string)reader["destination_table_schema"]); - } + string sourceColumnName = (string)reader["source_column"]; + string destinationColumnName = (string)reader["destination_column"]; - Dictionary name2table = new Dictionary(); - List tables = new List(); - foreach (ColumnDto column in columns) - { - if (!name2table.ContainsKey( column.TableName )) - name2table.Add( column.TableName, new TableDto( column.TableName ) ); + ForeignKey fk = new ForeignKey(sourceTable, sourceColumnName, destinationTable, destinationColumnName); - TableDto table = name2table[ column.TableName ]; - table.Add( column ); - tables.Add( table ); - } + foreignKeys.Add(fk); + } + } + } - return (tables.ToArray(), foreignKeys.ToArray()); - } - } + } + + return new DbmlSchema(tables, foreignKeys); + } + } } \ No newline at end of file diff --git a/src/DbDiagramIo.MsSql/Objects/Base/IDbmlObject.cs b/src/DbDiagramIo.MsSql/Objects/Base/IDbmlObject.cs new file mode 100644 index 0000000..bca2218 --- /dev/null +++ b/src/DbDiagramIo.MsSql/Objects/Base/IDbmlObject.cs @@ -0,0 +1,11 @@ +namespace DbDiagramIo.MsSql.Objects.Base +{ + /// + /// Defines object that can be converted to dbdiagram.io format + /// https://dbml.dbdiagram.io/home/#intro + /// + public interface IDbmlObject + { + string ToDbml(); + } +} \ No newline at end of file diff --git a/src/DbDiagramIo.MsSql/TableDto.cs b/src/DbDiagramIo.MsSql/TableDto.cs deleted file mode 100644 index e428de8..0000000 --- a/src/DbDiagramIo.MsSql/TableDto.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using System.Text; - -namespace DbDbiagramIo.MsSql -{ - public class TableDto - { - public string Name { get; } - - private readonly SortedList columns = new SortedList(); - - internal void Add( ColumnDto column ) - { - this.columns.Add( column.OrdinalPosition, column ); - } - - public TableDto( string name ) - { - Name = name; - } - - public string ToDbDbiagramCode() - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.AppendLine( $"Table \"{Name}\" {{" ); - - foreach (ColumnDto column in this.columns.Values) - { - stringBuilder.AppendLine( $" {column.ToDbDiagramCode()}" ); - } - - stringBuilder.AppendLine( "}" ); - - return stringBuilder.ToString(); - } - } -} diff --git a/src/Example.DotNetCore/Example.DotNetCore.csproj b/src/Example.DotNetCore/Example.DotNetCore.csproj index c551132..0eae296 100644 --- a/src/Example.DotNetCore/Example.DotNetCore.csproj +++ b/src/Example.DotNetCore/Example.DotNetCore.csproj @@ -5,6 +5,10 @@ netcoreapp3.1 + + + + diff --git a/src/Example.DotNetCore/Program.cs b/src/Example.DotNetCore/Program.cs index 2094b28..aa666a8 100644 --- a/src/Example.DotNetCore/Program.cs +++ b/src/Example.DotNetCore/Program.cs @@ -1,26 +1,30 @@ -using DbDbiagramIo.MsSql; +using DbDiagramIo.MsSql; using System; +using System.Linq; +using DbDiagramIo.MsSql.Objects; namespace Example.DotNetCore { - class Program - { - // TODO: set your own database connection string here - private const string __ConnectionString = "Server=(local)\\SQLEXPRESS;Database=master;Integrated Security=true;"; + class Program + { + private const string __ConnectionString = "Server=.;Database=LUP_Package;Integrated Security=true;"; - static void Main( string[] args ) - { - (TableDto[] tables, ForeignKeyDto[] foreignKeys) = MsSqlSchemaReader.ReadTablesAndForeignKeysFromDb( __ConnectionString ); + static void Main(string[] args) + { + var schemaReader = new MsSqlSchemaReader(__ConnectionString); + var schema = schemaReader.GetDbmlSchema(); + + foreach (Table table in schema.Tables.Where(t => t.Schema != "HangFire" && !t.Name.StartsWith("AspNet"))) + { + Console.WriteLine(table.ToDbml()); + } - foreach (TableDto table in tables) - { - Console.WriteLine( table.ToDbDbiagramCode() ); - } - - foreach (ForeignKeyDto fk in foreignKeys) - { - Console.WriteLine( fk.ToDbDiagramDto() ); - } - } - } + foreach (ForeignKey fk in schema.ForeignKeys.Where(f => f.SourceTable.Schema != "HangFire" && f.TargetTable.Schema != "HangFire" + && f.TargetTable.Name != "WinUsers" && f.SourceTable.Name != "WinUsers" + && !f.SourceTable.Name.StartsWith("AspNet")&& !f.TargetTable.Name.StartsWith("AspNet"))) + { + Console.WriteLine(fk.ToDbml()); + } + } + } } diff --git a/src/Example.Dotnet461/App.config b/src/Example.Dotnet461/App.config index 731f6de..4bfa005 100644 --- a/src/Example.Dotnet461/App.config +++ b/src/Example.Dotnet461/App.config @@ -1,6 +1,6 @@ - + - + - \ No newline at end of file + diff --git a/src/Example.Dotnet461/Example.Dotnet461.csproj b/src/Example.Dotnet461/Example.Dotnet461.csproj index bdc3763..09f63d5 100644 --- a/src/Example.Dotnet461/Example.Dotnet461.csproj +++ b/src/Example.Dotnet461/Example.Dotnet461.csproj @@ -8,10 +8,11 @@ Exe Example.Dotnet461 Example.Dotnet461 - v4.6.1 + v4.8 512 true true + AnyCPU diff --git a/src/Example.Dotnet461/Program.cs b/src/Example.Dotnet461/Program.cs index 0fb4134..bec60a9 100644 --- a/src/Example.Dotnet461/Program.cs +++ b/src/Example.Dotnet461/Program.cs @@ -1,26 +1,26 @@ -using DbDbiagramIo.MsSql; +using DbDiagramIo.MsSql; using System; namespace Example.Dotnet461 { - class Program - { - // TODO: set your own database connection string here - private const string __ConnectionString = "Server=(local)\\SQLEXPRESS;Database=master;Integrated Security=true;"; + class Program + { + // TODO: set your own database connection string here + private const string __ConnectionString = "Server=(local)\\SQLEXPRESS;Database=master;Integrated Security=true;"; - static void Main( string[] args ) - { - (TableDto[] tables, ForeignKeyDto[] foreignKeys) = MsSqlSchemaReader.ReadTablesAndForeignKeysFromDb( __ConnectionString ); + static void Main(string[] args) + { + (Table[] tables, ForeignKey[] foreignKeys) = MsSqlSchemaReader.GetSchemaDescriptor(__ConnectionString); - foreach (TableDto table in tables) - { - Console.WriteLine( table.ToDbDbiagramCode() ); - } + foreach (Table table in tables) + { + Console.WriteLine(table.ToDbDbiagramCode()); + } - foreach (ForeignKeyDto fk in foreignKeys) - { - Console.WriteLine( fk.ToDbDiagramDto() ); - } - } - } + foreach (ForeignKey fk in foreignKeys) + { + Console.WriteLine(fk.ToDbDiagramDto()); + } + } + } }