diff --git a/src/ConsoleTestQuickCompare/ConsoleTestQuickCompare.csproj b/src/ConsoleTestQuickCompare/ConsoleTestQuickCompare.csproj index d8045c7..6429cf8 100644 --- a/src/ConsoleTestQuickCompare/ConsoleTestQuickCompare.csproj +++ b/src/ConsoleTestQuickCompare/ConsoleTestQuickCompare.csproj @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/ConsoleTestQuickCompare/Program.cs b/src/ConsoleTestQuickCompare/Program.cs index b7cfff8..b93397e 100644 --- a/src/ConsoleTestQuickCompare/Program.cs +++ b/src/ConsoleTestQuickCompare/Program.cs @@ -8,6 +8,7 @@ namespace ConsoleTestQuickCompare; using System.Diagnostics; using Microsoft.Extensions.DependencyInjection; using QuickCompareModel; +using QuickCompareModel.Models; /// Main program loop. internal static class Program diff --git a/src/ConsoleTestQuickCompare/Startup.cs b/src/ConsoleTestQuickCompare/Startup.cs index 622963d..1003504 100644 --- a/src/ConsoleTestQuickCompare/Startup.cs +++ b/src/ConsoleTestQuickCompare/Startup.cs @@ -7,6 +7,7 @@ namespace ConsoleTestQuickCompare; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using QuickCompareModel; +using QuickCompareModel.Models; /// Initialises the dependency injection container from app settings. internal class Startup diff --git a/src/QuickCompareModel/DatabaseDifferences/BaseDifference.cs b/src/QuickCompareModel/DatabaseDifferences/BaseDifference.cs index a4451c2..e4a48d5 100644 --- a/src/QuickCompareModel/DatabaseDifferences/BaseDifference.cs +++ b/src/QuickCompareModel/DatabaseDifferences/BaseDifference.cs @@ -2,97 +2,100 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseDifferences +namespace QuickCompareModel.DatabaseDifferences; + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +/// Model to represent the most basic element and track the existence across two databases. +/// +/// Initialises a new instance of the class +/// with values determining whether the item exists in each database. +/// +/// Value indicating whether the item exists in database 1. +/// Value indicating whether the item exists in database 2. +public partial class BaseDifference(bool existsInDatabase1, bool existsInDatabase2) { - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Text.RegularExpressions; + /// Whitespace indentation used for output text. + protected const string TabIndent = " "; - /// Model to represent the most basic element and track the existence across two databases. - public class BaseDifference + /// Gets or sets a value indicating whether the item exists in database 1. + public bool ExistsInDatabase1 { get; set; } = existsInDatabase1; + + /// Gets or sets a value indicating whether the item exists in database 2. + public bool ExistsInDatabase2 { get; set; } = existsInDatabase2; + + /// Gets a value indicating whether the item exists in both databases. + public bool ExistsInBothDatabases => this.ExistsInDatabase1 && this.ExistsInDatabase2; + + /// Gets a value indicating whether there are any differences. + public virtual bool IsDifferent => !this.ExistsInBothDatabases; + + /// A helper method to trim whitespace and comments from text to reduce false-positive results. + /// Text to modify. + /// Value indicating where to reduce all whitespace to a single character. + /// Modified text ready for comparison. + public static string CleanDefinitionText(string definition, bool stripWhiteSpace) { - /// Whitespace indentation used for output text. - protected const string TabIndent = " "; - - /// - /// Initialises a new instance of the class - /// with values determining whether the item exists in each database. - /// - /// Value indicating whether the item exists in database 1. - /// Value indicating whether the item exists in database 2. - public BaseDifference(bool existsInDatabase1, bool existsInDatabase2) + if (string.IsNullOrWhiteSpace(definition)) { - this.ExistsInDatabase1 = existsInDatabase1; - this.ExistsInDatabase2 = existsInDatabase2; + return string.Empty; } - /// Gets or sets a value indicating whether the item exists in database 1. - public bool ExistsInDatabase1 { get; set; } + definition = StripMultiLineComments(definition); + definition = StripSingleLineComments(definition); - /// Gets or sets a value indicating whether the item exists in database 2. - public bool ExistsInDatabase2 { get; set; } + if (stripWhiteSpace) + { + definition = StripCommaWhitespace(definition); + definition = NormaliseCommas(definition); + definition = ReduceWhitespaceToSingleCharacter(definition); + } - /// Gets a value indicating whether the item exists in both databases. - public bool ExistsInBothDatabases => this.ExistsInDatabase1 && this.ExistsInDatabase2; + return definition.Trim(); + } - /// Gets a value indicating whether there are any differences. - public virtual bool IsDifferent => !this.ExistsInBothDatabases; + /// Gets a text description of the difference or returns an empty string if no difference is detected. + /// Difference description. + public override string ToString() => this.ExistsInBothDatabases ? string.Empty : $"does not exist in database {(this.ExistsInDatabase1 ? 2 : 1)}\r\n"; + + /// A helper method to list difference values for subsections. + /// Implementation of . + /// Difference collection. + /// Name of subsection. + /// Text output for subsection. + protected static string GetSubSectionDifferenceOutput(Dictionary source, string name) + where T : BaseDifference + { + var section = new StringBuilder(); - /// A helper method to trim whitespace and comments from text to reduce false-positive results. - /// Text to modify. - /// Value indicating where to reduce all whitespace to a single character. - /// Modified text ready for comparison. - public static string CleanDefinitionText(string definition, bool stripWhiteSpace) + foreach (var prop in source.Where(x => x.Value.IsDifferent)) { - if (string.IsNullOrWhiteSpace(definition)) - { - return string.Empty; - } - - definition = StripMultiLineComments(definition); - definition = StripSingleLineComments(definition); - - if (stripWhiteSpace) - { - definition = StripCommaWhitespace(definition); - definition = NormaliseCommas(definition); - definition = ReduceWhitespaceToSingleCharacter(definition); - } - - return definition.Trim(); + section.Append($"{TabIndent}{name}: {prop.Key} {prop.Value}"); } - /// Gets a text description of the difference or returns an empty string if no difference is detected. - /// Difference description. - public override string ToString() => this.ExistsInBothDatabases ? string.Empty : $"does not exist in database {(this.ExistsInDatabase1 ? 2 : 1)}\r\n"; - - /// A helper method to list difference values for subsections. - /// Implementation of . - /// Difference collection. - /// Name of subsection. - /// Text output for subsection. - protected static string GetSubSectionDifferenceOutput(Dictionary source, string name) - where T : BaseDifference - { - var section = new StringBuilder(); + return section.ToString(); + } - foreach (var prop in source.Where(x => x.Value.IsDifferent)) - { - section.Append($"{TabIndent}{name}: {prop.Key} {prop.Value}"); - } + private static string StripMultiLineComments(string input) => MultilineCommentRegex().Replace(input, string.Empty); - return section.ToString(); - } + private static string StripSingleLineComments(string input) => SingleLineCommentRegex().Replace(input, string.Empty); - private static string StripMultiLineComments(string input) => Regex.Replace(input, @"/\*[^*]*\*+([^/*][^*]*\*+)*/", string.Empty); + private static string StripCommaWhitespace(string input) => CommaWitespaceRegex().Replace(input, ","); - private static string StripSingleLineComments(string input) => Regex.Replace(input, @"(--)([^\r\n]+)", string.Empty); + private static string NormaliseCommas(string input) => CommaRegex().Replace(input, ", "); - private static string StripCommaWhitespace(string input) => Regex.Replace(input, @"\s*,\s*", ","); + private static string ReduceWhitespaceToSingleCharacter(string input) => WhitespaceRegex().Replace(input, " "); - private static string NormaliseCommas(string input) => Regex.Replace(input, @"[,]", ", "); + [GeneratedRegex(@"/\*[^*]*\*+([^/*][^*]*\*+)*/")] private static partial Regex MultilineCommentRegex(); - private static string ReduceWhitespaceToSingleCharacter(string input) => Regex.Replace(input, @"[\s]+", " "); - } + [GeneratedRegex(@"(--)([^\r\n]+)")] private static partial Regex SingleLineCommentRegex(); + + [GeneratedRegex(@"\s*,\s*")] private static partial Regex CommaWitespaceRegex(); + + [GeneratedRegex(@"[,]")] private static partial Regex CommaRegex(); + + [GeneratedRegex(@"[\s]+")] private static partial Regex WhitespaceRegex(); } diff --git a/src/QuickCompareModel/DatabaseDifferences/DatabaseObjectDifference.cs b/src/QuickCompareModel/DatabaseDifferences/DatabaseObjectDifference.cs index 1f11d69..5c4c9f4 100644 --- a/src/QuickCompareModel/DatabaseDifferences/DatabaseObjectDifference.cs +++ b/src/QuickCompareModel/DatabaseDifferences/DatabaseObjectDifference.cs @@ -2,73 +2,63 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseDifferences -{ - using System.Collections.Generic; - using System.Linq; - using System.Text; +namespace QuickCompareModel.DatabaseDifferences; - /// Model to represent a complex database element and track the differences across two databases. - public class DatabaseObjectDifference : BaseDifference - { - /// - /// Initialises a new instance of the class - /// with values determining whether the item exists in each database. - /// - /// Value indicating whether the item exists in database 1. - /// Value indicating whether the item exists in database 2. - public DatabaseObjectDifference(bool existsInDatabase1, bool existsInDatabase2) - : base(existsInDatabase1, existsInDatabase2) - { - } +using System.Collections.Generic; +using System.Linq; +using System.Text; - /// Gets or sets the body text of the object in database 1. - public string ObjectDefinition1 { get; set; } +/// Model to represent a complex database element and track the differences across two databases. +/// Value indicating whether the item exists in database 1. +/// Value indicating whether the item exists in database 2. +public class DatabaseObjectDifference(bool existsInDatabase1, bool existsInDatabase2) : BaseDifference(existsInDatabase1, existsInDatabase2) +{ + /// Gets or sets the body text of the object in database 1. + public string ObjectDefinition1 { get; set; } - /// Gets or sets the body text of the object in database 2. - public string ObjectDefinition2 { get; set; } + /// Gets or sets the body text of the object in database 2. + public string ObjectDefinition2 { get; set; } - /// Gets or sets a set of models to represent extended properties and track the differences across two databases. - public Dictionary ExtendedPropertyDifferences { get; set; } = new Dictionary(); + /// Gets or sets a set of models to represent extended properties and track the differences across two databases. + public Dictionary ExtendedPropertyDifferences { get; set; } = []; - /// Gets or sets a set of models to represent permissions and track the differences across two databases. - public Dictionary PermissionDifferences { get; set; } = new Dictionary(); + /// Gets or sets a set of models to represent permissions and track the differences across two databases. + public Dictionary PermissionDifferences { get; set; } = []; - /// Gets a value indicating whether the body text is different. - public bool DefinitionsAreDifferent => CleanDefinitionText(this.ObjectDefinition1, true) != CleanDefinitionText(this.ObjectDefinition2, true); + /// Gets a value indicating whether the body text is different. + public bool DefinitionsAreDifferent => CleanDefinitionText(this.ObjectDefinition1, true) != CleanDefinitionText(this.ObjectDefinition2, true); - /// Gets a value indicating whether any differences have been tracked. - public override bool IsDifferent => - base.IsDifferent || - this.DefinitionsAreDifferent || - this.PermissionDifferences.Values.Any(x => x.IsDifferent) || - this.ExtendedPropertyDifferences.Values.Any(x => x.IsDifferent); + /// Gets a value indicating whether any differences have been tracked. + public override bool IsDifferent => + base.IsDifferent || + this.DefinitionsAreDifferent || + this.PermissionDifferences.Values.Any(x => x.IsDifferent) || + this.ExtendedPropertyDifferences.Values.Any(x => x.IsDifferent); - /// Gets a text description of the difference or returns an empty string if no difference is detected. - /// Difference description. - public override string ToString() + /// Gets a text description of the difference or returns an empty string if no difference is detected. + /// Difference description. + public override string ToString() + { + if (!this.IsDifferent) { - if (!this.IsDifferent) - { - return string.Empty; - } + return string.Empty; + } - if (base.IsDifferent) - { - return base.ToString(); - } + if (base.IsDifferent) + { + return base.ToString(); + } - var sb = new StringBuilder("\r\n"); + var sb = new StringBuilder("\r\n"); - if (this.DefinitionsAreDifferent) - { - sb.AppendLine($"{TabIndent}Definitions are different"); - } + if (this.DefinitionsAreDifferent) + { + sb.AppendLine($"{TabIndent}Definitions are different"); + } - sb.Append(GetSubSectionDifferenceOutput(this.ExtendedPropertyDifferences, "Extended property")); - sb.Append(GetSubSectionDifferenceOutput(this.PermissionDifferences, "Permission")); + sb.Append(GetSubSectionDifferenceOutput(this.ExtendedPropertyDifferences, "Extended property")); + sb.Append(GetSubSectionDifferenceOutput(this.PermissionDifferences, "Permission")); - return sb.ToString(); - } + return sb.ToString(); } } diff --git a/src/QuickCompareModel/DatabaseDifferences/Differences.cs b/src/QuickCompareModel/DatabaseDifferences/Differences.cs index 91d79ce..818154d 100644 --- a/src/QuickCompareModel/DatabaseDifferences/Differences.cs +++ b/src/QuickCompareModel/DatabaseDifferences/Differences.cs @@ -2,105 +2,104 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseDifferences -{ - using System.Collections.Generic; - using System.Linq; - using System.Text; +namespace QuickCompareModel.DatabaseDifferences; + +using System.Collections.Generic; +using System.Linq; +using System.Text; - /// Model to hold lists of various differences between two databases. - public class Differences +/// Model to hold lists of various differences between two databases. +public class Differences +{ + /// Gets or sets a friendly name for database 1. + public string Database1 { get; set; } + + /// Gets or sets a friendly name for database 2. + public string Database2 { get; set; } + + /// Gets or sets a set of models to represent extended properties and track the differences across two databases. + public Dictionary ExtendedPropertyDifferences { get; set; } + = []; + + /// Gets or sets a set of models to represent permissions and track the differences across two databases. + public Dictionary PermissionDifferences { get; set; } + = []; + + /// Gets or sets a set of models to represent tables and track the differences across two databases. + public Dictionary TableDifferences { get; set; } + = []; + + /// Gets or sets a set of models to represent user types and track the differences across two databases. + public Dictionary UserTypeDifferences { get; set; } + = []; + + /// Gets or sets a set of models to represent functions and track the differences across two databases. + public Dictionary FunctionDifferences { get; set; } + = []; + + /// Gets or sets a set of models to represent stored procedures and track the differences across two databases. + public Dictionary StoredProcedureDifferences { get; set; } + = []; + + /// Gets or sets a set of models to represent views and track the differences across two databases. + public Dictionary ViewDifferences { get; set; } + = []; + + /// Gets or sets a set of models to represent synonyms and track the differences across two databases. + public Dictionary SynonymDifferences { get; set; } + = []; + + /// Gets a value indicating whether any differences have been tracked. + public bool HasDifferences => + this.ExtendedPropertyDifferences.Any(x => x.Value.IsDifferent) || + this.PermissionDifferences.Any(x => x.Value.IsDifferent) || + this.TableDifferences.Any(x => x.Value.IsDifferent) || + this.FunctionDifferences.Any(x => x.Value.IsDifferent) || + this.StoredProcedureDifferences.Any(x => x.Value.IsDifferent) || + this.ViewDifferences.Any(x => x.Value.IsDifferent) || + this.SynonymDifferences.Any(x => x.Value.IsDifferent); + + /// Gets a report of the differences, whether any were detected or not. + /// Difference description. + public override string ToString() { - /// Gets or sets a friendly name for database 1. - public string Database1 { get; set; } - - /// Gets or sets a friendly name for database 2. - public string Database2 { get; set; } - - /// Gets or sets a set of models to represent extended properties and track the differences across two databases. - public Dictionary ExtendedPropertyDifferences { get; set; } - = new Dictionary(); - - /// Gets or sets a set of models to represent permissions and track the differences across two databases. - public Dictionary PermissionDifferences { get; set; } - = new Dictionary(); - - /// Gets or sets a set of models to represent tables and track the differences across two databases. - public Dictionary TableDifferences { get; set; } - = new Dictionary(); - - /// Gets or sets a set of models to represent user types and track the differences across two databases. - public Dictionary UserTypeDifferences { get; set; } - = new Dictionary(); - - /// Gets or sets a set of models to represent functions and track the differences across two databases. - public Dictionary FunctionDifferences { get; set; } - = new Dictionary(); - - /// Gets or sets a set of models to represent stored procedures and track the differences across two databases. - public Dictionary StoredProcedureDifferences { get; set; } - = new Dictionary(); - - /// Gets or sets a set of models to represent views and track the differences across two databases. - public Dictionary ViewDifferences { get; set; } - = new Dictionary(); - - /// Gets or sets a set of models to represent synonyms and track the differences across two databases. - public Dictionary SynonymDifferences { get; set; } - = new Dictionary(); - - /// Gets a value indicating whether any differences have been tracked. - public bool HasDifferences => - this.ExtendedPropertyDifferences.Any(x => x.Value.IsDifferent) || - this.PermissionDifferences.Any(x => x.Value.IsDifferent) || - this.TableDifferences.Any(x => x.Value.IsDifferent) || - this.FunctionDifferences.Any(x => x.Value.IsDifferent) || - this.StoredProcedureDifferences.Any(x => x.Value.IsDifferent) || - this.ViewDifferences.Any(x => x.Value.IsDifferent) || - this.SynonymDifferences.Any(x => x.Value.IsDifferent); - - /// Gets a report of the differences, whether any were detected or not. - /// Difference description. - public override string ToString() - { - var output = new StringBuilder("QuickCompare schema comparison result\r\n\r\n"); - output.AppendLine($"Database 1: {this.Database1}"); - output.AppendLine($"Database 2: {this.Database2}\r\n"); - - if (!this.HasDifferences) - { - output.AppendLine("NO DIFFERENCES HAVE BEEN FOUND"); - return output.ToString(); - } - - output.Append(GetSectionDifferenceOutput(this.ExtendedPropertyDifferences, "Extended property")); - output.Append(GetSectionDifferenceOutput(this.PermissionDifferences, "Permission")); - output.Append(GetSectionDifferenceOutput(this.TableDifferences, "Table")); - output.Append(GetSectionDifferenceOutput(this.UserTypeDifferences, "User type")); - output.Append(GetSectionDifferenceOutput(this.ViewDifferences, "View")); - output.Append(GetSectionDifferenceOutput(this.FunctionDifferences, "Function")); - output.Append(GetSectionDifferenceOutput(this.StoredProcedureDifferences, "Stored procedure")); - output.Append(GetSectionDifferenceOutput(this.SynonymDifferences, "Synonym")); + var output = new StringBuilder("QuickCompare schema comparison result\r\n\r\n"); + output.AppendLine($"Database 1: {this.Database1}"); + output.AppendLine($"Database 2: {this.Database2}\r\n"); + if (!this.HasDifferences) + { + output.AppendLine("NO DIFFERENCES HAVE BEEN FOUND"); return output.ToString(); } - private static string GetSectionDifferenceOutput(Dictionary source, string name) - where T : BaseDifference - { - var section = new StringBuilder(); + output.Append(GetSectionDifferenceOutput(this.ExtendedPropertyDifferences, "Extended property")); + output.Append(GetSectionDifferenceOutput(this.PermissionDifferences, "Permission")); + output.Append(GetSectionDifferenceOutput(this.TableDifferences, "Table")); + output.Append(GetSectionDifferenceOutput(this.UserTypeDifferences, "User type")); + output.Append(GetSectionDifferenceOutput(this.ViewDifferences, "View")); + output.Append(GetSectionDifferenceOutput(this.FunctionDifferences, "Function")); + output.Append(GetSectionDifferenceOutput(this.StoredProcedureDifferences, "Stored procedure")); + output.Append(GetSectionDifferenceOutput(this.SynonymDifferences, "Synonym")); - foreach (var prop in source.Where(x => x.Value.IsDifferent)) - { - section.Append($"{name}: {prop.Key} {prop.Value}"); - } + return output.ToString(); + } - if (section.Length > 0) - { - section.Insert(0, $"\r\n{name.ToUpperInvariant()} DIFFERENCES\r\n\r\n"); - } + private static string GetSectionDifferenceOutput(Dictionary source, string name) + where T : BaseDifference + { + var section = new StringBuilder(); + + foreach (var prop in source.Where(x => x.Value.IsDifferent)) + { + section.Append($"{name}: {prop.Key} {prop.Value}"); + } - return section.ToString(); + if (section.Length > 0) + { + section.Insert(0, $"\r\n{name.ToUpperInvariant()} DIFFERENCES\r\n\r\n"); } + + return section.ToString(); } } diff --git a/src/QuickCompareModel/DatabaseDifferences/ExtendedPropertyDifference.cs b/src/QuickCompareModel/DatabaseDifferences/ExtendedPropertyDifference.cs index 6768191..75323c6 100644 --- a/src/QuickCompareModel/DatabaseDifferences/ExtendedPropertyDifference.cs +++ b/src/QuickCompareModel/DatabaseDifferences/ExtendedPropertyDifference.cs @@ -2,35 +2,25 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseDifferences -{ - /// Model to represent an extended property and track the differences across two databases. - public class ExtendedPropertyDifference : BaseDifference - { - /// - /// Initialises a new instance of the class - /// with values determining whether the item exists in each database. - /// - /// Value indicating whether the item exists in database 1. - /// Value indicating whether the item exists in database 2. - public ExtendedPropertyDifference(bool existsInDatabase1, bool existsInDatabase2) - : base(existsInDatabase1, existsInDatabase2) - { - } +namespace QuickCompareModel.DatabaseDifferences; - /// Gets or sets the property value for database 1. - public string Value1 { get; set; } +/// Model to represent an extended property and track the differences across two databases. +/// Value indicating whether the item exists in database 1. +/// Value indicating whether the item exists in database 2. +public class ExtendedPropertyDifference(bool existsInDatabase1, bool existsInDatabase2) : BaseDifference(existsInDatabase1, existsInDatabase2) +{ + /// Gets or sets the property value for database 1. + public string Value1 { get; set; } - /// Gets or sets the property value for database 2. - public string Value2 { get; set; } + /// Gets or sets the property value for database 2. + public string Value2 { get; set; } - /// Gets a value indicating whether any differences have been tracked. - public override bool IsDifferent => base.IsDifferent || this.Value1 != this.Value2; + /// Gets a value indicating whether any differences have been tracked. + public override bool IsDifferent => base.IsDifferent || this.Value1 != this.Value2; - /// Gets a text description of the difference or returns an empty string if no difference is detected. - /// Difference description. - public override string ToString() => this.IsDifferent - ? base.IsDifferent ? base.ToString() : $"value is different; [{this.Value1}] in database 1, [{this.Value2}] in database 2\r\n" - : string.Empty; - } + /// Gets a text description of the difference or returns an empty string if no difference is detected. + /// Difference description. + public override string ToString() => this.IsDifferent + ? base.IsDifferent ? base.ToString() : $"value is different; [{this.Value1}] in database 1, [{this.Value2}] in database 2\r\n" + : string.Empty; } diff --git a/src/QuickCompareModel/DatabaseDifferences/ItemDifference.cs b/src/QuickCompareModel/DatabaseDifferences/ItemDifference.cs index 45e7b06..47ca6b6 100644 --- a/src/QuickCompareModel/DatabaseDifferences/ItemDifference.cs +++ b/src/QuickCompareModel/DatabaseDifferences/ItemDifference.cs @@ -2,64 +2,54 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseDifferences +namespace QuickCompareModel.DatabaseDifferences; + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +/// +/// Model to represent an element that belongs to a table. +/// +/// Value indicating whether the item exists in database 1. +/// Value indicating whether the item exists in database 2. +public class ItemDifference(bool existsInDatabase1, bool existsInDatabase2) : BaseDifference(existsInDatabase1, existsInDatabase2) { - using System.Collections.Generic; - using System.Linq; - using System.Text; + /// Gets or sets a list of tracked difference strings. + public List Differences { get; set; } = []; - /// - /// Model to represent an element that belongs to a table. - /// - public class ItemDifference : BaseDifference - { - /// - /// Initialises a new instance of the class - /// with values determining whether the item exists in each database. - /// - /// Value indicating whether the item exists in database 1. - /// Value indicating whether the item exists in database 2. - public ItemDifference(bool existsInDatabase1, bool existsInDatabase2) - : base(existsInDatabase1, existsInDatabase2) - { - } + /// Gets or sets the item type. + public string ItemType { get; set; } - /// Gets or sets a list of tracked difference strings. - public List Differences { get; set; } = new List(); + /// Gets a value indicating whether any differences have been tracked. + public override bool IsDifferent => base.IsDifferent || this.Differences.Count > 0; - /// Gets or sets the item type. - public string ItemType { get; set; } - - /// Gets a value indicating whether any differences have been tracked. - public override bool IsDifferent => base.IsDifferent || this.Differences.Count > 0; + /// Gets a text description of the list of differences or returns an empty string if no difference is detected. + /// Ttext description of the list of differences. + public string DifferenceList() + { + var sb = new StringBuilder(); - /// Gets a text description of the list of differences or returns an empty string if no difference is detected. - /// Ttext description of the list of differences. - public string DifferenceList() + if (this.Differences.Count == 1) { - var sb = new StringBuilder(); - - if (this.Differences.Count == 1) - { - sb.AppendLine(this.Differences.Single()); - } - else if (this.Differences.Count > 1) - { - sb.Append($"\r\n{TabIndent} - "); - sb.AppendLine(string.Join($"\r\n{TabIndent} - ", this.Differences.ToArray())); - } - else - { - sb.Append("\r\n"); // (only ItemWithPropertiesDifference has output) - } - - return sb.ToString(); + sb.AppendLine(this.Differences.Single()); + } + else if (this.Differences.Count > 1) + { + sb.Append($"\r\n{TabIndent} - "); + sb.AppendLine(string.Join($"\r\n{TabIndent} - ", this.Differences.ToArray())); + } + else + { + sb.Append("\r\n"); // (only ItemWithPropertiesDifference has output) } - /// Gets a text description of the differences or returns an empty string if no difference is detected. - /// Difference description. - public override string ToString() => this.IsDifferent - ? base.IsDifferent ? base.ToString() : this.DifferenceList() - : string.Empty; + return sb.ToString(); } + + /// Gets a text description of the differences or returns an empty string if no difference is detected. + /// Difference description. + public override string ToString() => this.IsDifferent + ? base.IsDifferent ? base.ToString() : this.DifferenceList() + : string.Empty; } diff --git a/src/QuickCompareModel/DatabaseDifferences/ItemWithPropertiesDifference.cs b/src/QuickCompareModel/DatabaseDifferences/ItemWithPropertiesDifference.cs index 019ce05..9c860f7 100644 --- a/src/QuickCompareModel/DatabaseDifferences/ItemWithPropertiesDifference.cs +++ b/src/QuickCompareModel/DatabaseDifferences/ItemWithPropertiesDifference.cs @@ -2,62 +2,61 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseDifferences +namespace QuickCompareModel.DatabaseDifferences; + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +/// Model to represent an element that can have extended properties and belongs to a table. +public class ItemWithPropertiesDifference + : ItemDifference { - using System.Collections.Generic; - using System.Linq; - using System.Text; + /// + /// Initialises a new instance of the class + /// with values determining whether the item exists in each database. + /// + /// Value indicating whether the item exists in database 1. + /// Value indicating whether the item exists in database 2. + public ItemWithPropertiesDifference(bool existsInDatabase1, bool existsInDatabase2) + : base(existsInDatabase1, existsInDatabase2) + { + } - /// Model to represent an element that can have extended properties and belongs to a table. - public class ItemWithPropertiesDifference - : ItemDifference + /// + /// Initialises a new instance of the class + /// with values determining the type and whether the item exists in each database. + /// + /// Value indicating whether the item exists in database 1. + /// Value indicating whether the item exists in database 2. + /// String describing the element type. + public ItemWithPropertiesDifference(bool existsInDatabase1, bool existsInDatabase2, string itemType) + : base(existsInDatabase1, existsInDatabase2) => this.ItemType = itemType; + + /// Gets or sets a set of models to represent extended properties and track the differences across two databases. + public Dictionary ExtendedPropertyDifferences { get; set; } + = []; + + /// Gets a value indicating whether any differences have been tracked. + public override bool IsDifferent => base.IsDifferent || this.ExtendedPropertyDifferences.Values.Any(x => x.IsDifferent); + + /// Gets a text description of the difference or returns an empty string if no difference is detected. + /// Difference description. + public override string ToString() { - /// - /// Initialises a new instance of the class - /// with values determining whether the item exists in each database. - /// - /// Value indicating whether the item exists in database 1. - /// Value indicating whether the item exists in database 2. - public ItemWithPropertiesDifference(bool existsInDatabase1, bool existsInDatabase2) - : base(existsInDatabase1, existsInDatabase2) + if (!this.IsDifferent) { + return string.Empty; } - /// - /// Initialises a new instance of the class - /// with values determining the type and whether the item exists in each database. - /// - /// Value indicating whether the item exists in database 1. - /// Value indicating whether the item exists in database 2. - /// String describing the element type. - public ItemWithPropertiesDifference(bool existsInDatabase1, bool existsInDatabase2, string itemType) - : base(existsInDatabase1, existsInDatabase2) => this.ItemType = itemType; - - /// Gets or sets a set of models to represent extended properties and track the differences across two databases. - public Dictionary ExtendedPropertyDifferences { get; set; } - = new Dictionary(); - - /// Gets a value indicating whether any differences have been tracked. - public override bool IsDifferent => base.IsDifferent || this.ExtendedPropertyDifferences.Values.Any(x => x.IsDifferent); - - /// Gets a text description of the difference or returns an empty string if no difference is detected. - /// Difference description. - public override string ToString() + if (!this.ExistsInBothDatabases) { - if (!this.IsDifferent) - { - return string.Empty; - } - - if (!this.ExistsInBothDatabases) - { - return base.ToString(); - } + return base.ToString(); + } - var sb = new StringBuilder(base.ToString()); - sb.Append(GetSubSectionDifferenceOutput(this.ExtendedPropertyDifferences, "Extended property")); + var sb = new StringBuilder(base.ToString()); + sb.Append(GetSubSectionDifferenceOutput(this.ExtendedPropertyDifferences, "Extended property")); - return sb.ToString(); - } + return sb.ToString(); } } diff --git a/src/QuickCompareModel/DatabaseDifferences/TableDifference.cs b/src/QuickCompareModel/DatabaseDifferences/TableDifference.cs index a3ab1ba..22dce3b 100644 --- a/src/QuickCompareModel/DatabaseDifferences/TableDifference.cs +++ b/src/QuickCompareModel/DatabaseDifferences/TableDifference.cs @@ -2,88 +2,72 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseDifferences -{ - using System.Collections.Generic; - using System.Linq; - using System.Text; +namespace QuickCompareModel.DatabaseDifferences; - /// Model to represent the table element and track the differences across two databases. - public class TableDifference : BaseDifference - { - /// - /// Initialises a new instance of the class - /// with values determining whether the item exists in each database. - /// - /// Value indicating whether the item exists in database 1. - /// Value indicating whether the item exists in database 2. - public TableDifference(bool existsInDatabase1, bool existsInDatabase2) - : base(existsInDatabase1, existsInDatabase2) - { - } +using System.Collections.Generic; +using System.Linq; +using System.Text; - /// Gets or sets a set of models to represent columns and track the differences across two databases. - public Dictionary ColumnDifferences { get; set; } - = new Dictionary(); +/// Model to represent the table element and track the differences across two databases. +/// Value indicating whether the item exists in database 1. +/// Value indicating whether the item exists in database 2. +public class TableDifference(bool existsInDatabase1, bool existsInDatabase2) : BaseDifference(existsInDatabase1, existsInDatabase2) +{ + /// Gets or sets a set of models to represent columns and track the differences across two databases. + public Dictionary ColumnDifferences { get; set; } = []; - /// Gets or sets a set of models to represent columns and track the differences across two databases. - public Dictionary RelationshipDifferences { get; set; } - = new Dictionary(); + /// Gets or sets a set of models to represent columns and track the differences across two databases. + public Dictionary RelationshipDifferences { get; set; } = []; - /// Gets or sets a set of models to represent indexes and track the differences across two databases. - public Dictionary IndexDifferences { get; set; } - = new Dictionary(); + /// Gets or sets a set of models to represent indexes and track the differences across two databases. + public Dictionary IndexDifferences { get; set; } = []; - /// Gets or sets a set of models to represent triggers and track the differences across two databases. - public Dictionary TriggerDifferences { get; set; } - = new Dictionary(); + /// Gets or sets a set of models to represent triggers and track the differences across two databases. + public Dictionary TriggerDifferences { get; set; } = []; - /// Gets or sets a set of models to represent extended properties and track the differences across two databases. - public Dictionary ExtendedPropertyDifferences { get; set; } - = new Dictionary(); + /// Gets or sets a set of models to represent extended properties and track the differences across two databases. + public Dictionary ExtendedPropertyDifferences { get; set; } = []; - /// Gets or sets a set of models to represent permissions and track the differences across two databases. - public Dictionary PermissionDifferences { get; set; } - = new Dictionary(); + /// Gets or sets a set of models to represent permissions and track the differences across two databases. + public Dictionary PermissionDifferences { get; set; } = []; - /// Gets a value indicating whether any differences have been tracked. - public override bool IsDifferent => - base.IsDifferent || - this.ColumnDifferences.Values.Any(x => x.IsDifferent) || - this.RelationshipDifferences.Values.Any(x => x.IsDifferent) || - this.IndexDifferences.Values.Any(x => x.IsDifferent) || - this.TriggerDifferences.Values.Any(x => x.IsDifferent) || - this.PermissionDifferences.Values.Any(x => x.IsDifferent) || - this.ExtendedPropertyDifferences.Values.Any(x => x.IsDifferent); + /// Gets a value indicating whether any differences have been tracked. + public override bool IsDifferent => + base.IsDifferent || + this.ColumnDifferences.Values.Any(x => x.IsDifferent) || + this.RelationshipDifferences.Values.Any(x => x.IsDifferent) || + this.IndexDifferences.Values.Any(x => x.IsDifferent) || + this.TriggerDifferences.Values.Any(x => x.IsDifferent) || + this.PermissionDifferences.Values.Any(x => x.IsDifferent) || + this.ExtendedPropertyDifferences.Values.Any(x => x.IsDifferent); - /// Gets a text description of the difference or returns an empty string if no difference is detected. - /// Difference description. - public override string ToString() + /// Gets a text description of the difference or returns an empty string if no difference is detected. + /// Difference description. + public override string ToString() + { + if (!this.IsDifferent) { - if (!this.IsDifferent) - { - return string.Empty; - } - - if (base.IsDifferent) - { - return base.ToString(); - } + return string.Empty; + } - var section = new StringBuilder("\r\n"); + if (base.IsDifferent) + { + return base.ToString(); + } - section.Append(GetSubSectionDifferenceOutput(this.ColumnDifferences, "Column")); - section.Append(GetSubSectionDifferenceOutput(this.TriggerDifferences, "Trigger")); - section.Append(GetSubSectionDifferenceOutput(this.RelationshipDifferences, "Relation")); - section.Append(GetSubSectionDifferenceOutput(this.ExtendedPropertyDifferences, "Extended property")); - section.Append(GetSubSectionDifferenceOutput(this.PermissionDifferences, "Permission")); + var section = new StringBuilder("\r\n"); - foreach (var indexDiff in this.IndexDifferences.Where(x => x.Value.IsDifferent)) - { - section.Append($"{TabIndent}{indexDiff.Value.ItemType}: {indexDiff.Key} {indexDiff.Value}"); - } + section.Append(GetSubSectionDifferenceOutput(this.ColumnDifferences, "Column")); + section.Append(GetSubSectionDifferenceOutput(this.TriggerDifferences, "Trigger")); + section.Append(GetSubSectionDifferenceOutput(this.RelationshipDifferences, "Relation")); + section.Append(GetSubSectionDifferenceOutput(this.ExtendedPropertyDifferences, "Extended property")); + section.Append(GetSubSectionDifferenceOutput(this.PermissionDifferences, "Permission")); - return section.ToString(); + foreach (var indexDiff in this.IndexDifferences.Where(x => x.Value.IsDifferent)) + { + section.Append($"{TabIndent}{indexDiff.Value.ItemType}: {indexDiff.Key} {indexDiff.Value}"); } + + return section.ToString(); } } diff --git a/src/QuickCompareModel/DatabaseInstance.cs b/src/QuickCompareModel/DatabaseInstance.cs deleted file mode 100644 index b795782..0000000 --- a/src/QuickCompareModel/DatabaseInstance.cs +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) Dan Ware. All rights reserved. -// - -namespace QuickCompareModel -{ - /// Enumeration to define which database is in scope. - public enum DatabaseInstance - { - /// Value is not defined. - Unknown, - - /// The first database in the comparison. - Database1, - - /// The second database in the comparison. - Database2, - } -} diff --git a/src/QuickCompareModel/DatabaseSchema/DataReaderExtensions.cs b/src/QuickCompareModel/DatabaseSchema/DataReaderExtensions.cs index 792098a..b6f063b 100644 --- a/src/QuickCompareModel/DatabaseSchema/DataReaderExtensions.cs +++ b/src/QuickCompareModel/DatabaseSchema/DataReaderExtensions.cs @@ -2,55 +2,54 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +using System.Data.SqlClient; + +/// +/// Set of extensions to improve cognitive complexity when dealing with DBNull to null conversions. +/// +public static class DataReaderExtensions { - using System.Data.SqlClient; - - /// - /// Set of extensions to improve cognitive complexity when dealing with DBNull to null conversions. - /// - public static class DataReaderExtensions - { - /// Gets the value of the specified column as a string or a null. - /// The current instance of . - /// The zero-based column ordinal. - /// The value of the specified column. - public static string GetNullableString(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : dr.GetString(index); - - /// Gets the value of the specified column as a byte or a null. - /// The current instance of . - /// The zero-based column ordinal. - /// The value of the specified column. - public static int? GetNullableByte(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : (int?)dr.GetByte(index); - - /// Gets the value of the specified column as a decimal or a null. - /// The current instance of . - /// The zero-based column ordinal. - /// The value of the specified column. - public static decimal? GetNullableDecimal(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : (decimal?)dr.GetDecimal(index); - - /// Gets the value of the specified column as an Int16 or a null. - /// The current instance of . - /// The zero-based column ordinal. - /// The value of the specified column. - public static int? GetNullableInt16(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : (int?)dr.GetInt16(index); - - /// Gets the value of the specified column as an Int32 or a null. - /// The current instance of . - /// The zero-based column ordinal. - /// The value of the specified column. - public static int? GetNullableInt32(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : (int?)dr.GetInt32(index); - - /// Gets the value of the specified column as an Int32 and returns a Boolean. - /// The current instance of . - /// The zero-based column ordinal. - /// The interpreted value of the specified column. - public static bool GetNullableInt32AsBoolean(this SqlDataReader dr, int index) => !dr.IsDBNull(index) && dr.GetInt32(index) == 1; - - /// Gets the value of the specified column as an Int32 and returns a Boolean. - /// The current instance of . - /// The zero-based column ordinal. - /// The interpreted value of the specified column. - public static bool GetInt32AsBoolean(this SqlDataReader dr, int index) => dr.GetInt32(index) == 1; - } + /// Gets the value of the specified column as a string or a null. + /// The current instance of . + /// The zero-based column ordinal. + /// The value of the specified column. + public static string GetNullableString(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : dr.GetString(index); + + /// Gets the value of the specified column as a byte or a null. + /// The current instance of . + /// The zero-based column ordinal. + /// The value of the specified column. + public static int? GetNullableByte(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : (int?)dr.GetByte(index); + + /// Gets the value of the specified column as a decimal or a null. + /// The current instance of . + /// The zero-based column ordinal. + /// The value of the specified column. + public static decimal? GetNullableDecimal(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : (decimal?)dr.GetDecimal(index); + + /// Gets the value of the specified column as an Int16 or a null. + /// The current instance of . + /// The zero-based column ordinal. + /// The value of the specified column. + public static int? GetNullableInt16(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : (int?)dr.GetInt16(index); + + /// Gets the value of the specified column as an Int32 or a null. + /// The current instance of . + /// The zero-based column ordinal. + /// The value of the specified column. + public static int? GetNullableInt32(this SqlDataReader dr, int index) => dr.IsDBNull(index) ? null : (int?)dr.GetInt32(index); + + /// Gets the value of the specified column as an Int32 and returns a Boolean. + /// The current instance of . + /// The zero-based column ordinal. + /// The interpreted value of the specified column. + public static bool GetNullableInt32AsBoolean(this SqlDataReader dr, int index) => !dr.IsDBNull(index) && dr.GetInt32(index) == 1; + + /// Gets the value of the specified column as an Int32 and returns a Boolean. + /// The current instance of . + /// The zero-based column ordinal. + /// The interpreted value of the specified column. + public static bool GetInt32AsBoolean(this SqlDataReader dr, int index) => dr.GetInt32(index) == 1; } diff --git a/src/QuickCompareModel/DatabaseSchema/Enums/PermissionObjectType.cs b/src/QuickCompareModel/DatabaseSchema/Enums/PermissionObjectType.cs index 440eeb9..ce0359c 100644 --- a/src/QuickCompareModel/DatabaseSchema/Enums/PermissionObjectType.cs +++ b/src/QuickCompareModel/DatabaseSchema/Enums/PermissionObjectType.cs @@ -2,35 +2,34 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema.Enums +namespace QuickCompareModel.DatabaseSchema.Enums; + +/// +/// Object type that permission applies to. +/// +public enum PermissionObjectType { - /// - /// Object type that permission applies to. - /// - public enum PermissionObjectType - { - /// Unknown. - Unknown, + /// Unknown. + Unknown, - /// Database. - Database, + /// Database. + Database, - /// SQL stored procedure. - SqlStoredProcedure, + /// SQL stored procedure. + SqlStoredProcedure, - /// SQL function. - SqlFunction, + /// SQL function. + SqlFunction, - /// SQL synonym. - Synonym, + /// SQL synonym. + Synonym, - /// SQL user table. - UserTable, + /// SQL user table. + UserTable, - /// SQL system table. - SystemTable, + /// SQL system table. + SystemTable, - /// SQL view. - View, - } + /// SQL view. + View, } diff --git a/src/QuickCompareModel/DatabaseSchema/Enums/PropertyObjectType.cs b/src/QuickCompareModel/DatabaseSchema/Enums/PropertyObjectType.cs index 16b2619..d0cec0f 100644 --- a/src/QuickCompareModel/DatabaseSchema/Enums/PropertyObjectType.cs +++ b/src/QuickCompareModel/DatabaseSchema/Enums/PropertyObjectType.cs @@ -2,29 +2,28 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema.Enums +namespace QuickCompareModel.DatabaseSchema.Enums; + +/// +/// Object type that extended property applies to. +/// +public enum PropertyObjectType { - /// - /// Object type that extended property applies to. - /// - public enum PropertyObjectType - { - /// Database. - Database, + /// Database. + Database, - /// Routine. - Routine, + /// Routine. + Routine, - /// Routine column. - RoutineColumn, + /// Routine column. + RoutineColumn, - /// Table. - Table, + /// Table. + Table, - /// Table column. - TableColumn, + /// Table column. + TableColumn, - /// Index. - Index, - } + /// Index. + Index, } diff --git a/src/QuickCompareModel/DatabaseSchema/SchemaNameExtensions.cs b/src/QuickCompareModel/DatabaseSchema/SchemaNameExtensions.cs index 8e927c2..443ff8b 100644 --- a/src/QuickCompareModel/DatabaseSchema/SchemaNameExtensions.cs +++ b/src/QuickCompareModel/DatabaseSchema/SchemaNameExtensions.cs @@ -2,52 +2,51 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +/// Extensions to derive the schema and object names from a full object ID. +public static class SchemaNameExtensions { - /// Extensions to derive the schema and object names from a full object ID. - public static class SchemaNameExtensions + /// Returns the schema name from a full object ID. + /// Full object ID. + /// Schema name or empty string. + public static string GetSchemaName(this string input) { - /// Returns the schema name from a full object ID. - /// Full object ID. - /// Schema name or empty string. - public static string GetSchemaName(this string input) + if (string.IsNullOrWhiteSpace(input)) { - if (string.IsNullOrWhiteSpace(input)) - { - return string.Empty; - } - - string[] parts = input.Split('.'); - return parts.Length != 2 ? string.Empty : parts[0].StripSquareBrackets(); + return string.Empty; } - /// Returns the object name from a full object ID. - /// Full object ID. - /// Object name or empty string. - public static string GetObjectName(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - { - return string.Empty; - } + string[] parts = input.Split('.'); + return parts.Length != 2 ? string.Empty : parts[0].StripSquareBrackets(); + } - string[] parts = input.Split('.'); - return parts.Length != 2 ? string.Empty : parts[1].StripSquareBrackets(); + /// Returns the object name from a full object ID. + /// Full object ID. + /// Object name or empty string. + public static string GetObjectName(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + { + return string.Empty; } - /// Adds the schema name to the beginning of the string. - /// Source object name. - /// Schema name to prepend. - /// A dot-delimited string containing the schema name and object name. - public static string PrependSchemaName(this string objectName, string schemaName) => - string.IsNullOrEmpty(schemaName) ? objectName : $"[{schemaName}].[{objectName}]"; - - /// / Removes instances of square bracket characters from the string. - /// Source input. - /// The source input without square bracket characters. - public static string StripSquareBrackets(this string input) => - input - .Replace("[", string.Empty) - .Replace("]", string.Empty); + string[] parts = input.Split('.'); + return parts.Length != 2 ? string.Empty : parts[1].StripSquareBrackets(); } + + /// Adds the schema name to the beginning of the string. + /// Source object name. + /// Schema name to prepend. + /// A dot-delimited string containing the schema name and object name. + public static string PrependSchemaName(this string objectName, string schemaName) => + string.IsNullOrEmpty(schemaName) ? objectName : $"[{schemaName}].[{objectName}]"; + + /// / Removes instances of square bracket characters from the string. + /// Source input. + /// The source input without square bracket characters. + public static string StripSquareBrackets(this string input) => + input + .Replace("[", string.Empty) + .Replace("]", string.Empty); } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlColumnDetail.cs b/src/QuickCompareModel/DatabaseSchema/SqlColumnDetail.cs index 00a4664..9ada10e 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlColumnDetail.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlColumnDetail.cs @@ -2,86 +2,85 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +/// +/// Class representing properties of a table column. +/// +public class SqlColumnDetail { - /// - /// Class representing properties of a table column. - /// - public class SqlColumnDetail - { - /// Gets or sets the table schema. - public string TableSchema { get; set; } + /// Gets or sets the table schema. + public string TableSchema { get; set; } - /// Gets or sets the table name. - public string TableName { get; set; } + /// Gets or sets the table name. + public string TableName { get; set; } - /// Gets or sets the column name. - public string ColumnName { get; set; } + /// Gets or sets the column name. + public string ColumnName { get; set; } - /// Gets or sets the ordinal position. - public int OrdinalPosition { get; set; } + /// Gets or sets the ordinal position. + public int OrdinalPosition { get; set; } - /// Gets or sets the column default. - public string ColumnDefault { get; set; } + /// Gets or sets the column default. + public string ColumnDefault { get; set; } - /// Gets or sets a value indicating whether is nullable. - public bool IsNullable { get; set; } + /// Gets or sets a value indicating whether is nullable. + public bool IsNullable { get; set; } - /// Gets or sets the datatype. - public string DataType { get; set; } + /// Gets or sets the datatype. + public string DataType { get; set; } - /// Gets or sets the character maximum length. - public int? CharacterMaximumLength { get; set; } + /// Gets or sets the character maximum length. + public int? CharacterMaximumLength { get; set; } - /// Gets or sets the character octet length. - public int? CharacterOctetLength { get; set; } + /// Gets or sets the character octet length. + public int? CharacterOctetLength { get; set; } - /// Gets or sets the numeric precision. - public int? NumericPrecision { get; set; } + /// Gets or sets the numeric precision. + public int? NumericPrecision { get; set; } - /// Gets or sets the numeric precesion radix. - public int? NumericPrecisionRadix { get; set; } + /// Gets or sets the numeric precesion radix. + public int? NumericPrecisionRadix { get; set; } - /// Gets or sets the numeric scale. - public int? NumericScale { get; set; } + /// Gets or sets the numeric scale. + public int? NumericScale { get; set; } - /// Gets or sets the datetime precision. - public int? DatetimePrecision { get; set; } + /// Gets or sets the datetime precision. + public int? DatetimePrecision { get; set; } - /// Gets or sets the character set name. - public string CharacterSetName { get; set; } + /// Gets or sets the character set name. + public string CharacterSetName { get; set; } - /// Gets or sets the collation name. - public string CollationName { get; set; } + /// Gets or sets the collation name. + public string CollationName { get; set; } - /// Gets or sets the domain schema. - public string DomainSchema { get; set; } + /// Gets or sets the domain schema. + public string DomainSchema { get; set; } - /// Gets or sets the domain name. - public string DomainName { get; set; } + /// Gets or sets the domain name. + public string DomainName { get; set; } - /// Gets or sets a value indicating whether is full-text indexed. - public bool IsFullTextIndexed { get; set; } + /// Gets or sets a value indicating whether is full-text indexed. + public bool IsFullTextIndexed { get; set; } - /// Gets or sets a value indicating whether is computed. - public bool IsComputed { get; set; } + /// Gets or sets a value indicating whether is computed. + public bool IsComputed { get; set; } - /// Gets or sets a value indicating whether is identity. - public bool IsIdentity { get; set; } + /// Gets or sets a value indicating whether is identity. + public bool IsIdentity { get; set; } - /// Gets or sets the identity seed. - public decimal? IdentitySeed { get; set; } + /// Gets or sets the identity seed. + public decimal? IdentitySeed { get; set; } - /// Gets or sets the identity increment. - public decimal? IdentityIncrement { get; set; } + /// Gets or sets the identity increment. + public decimal? IdentityIncrement { get; set; } - /// Gets or sets a value indicating whether is sparse. - public bool IsSparse { get; set; } + /// Gets or sets a value indicating whether is sparse. + public bool IsSparse { get; set; } - /// Gets or sets a value indicating whether is column set. - public bool IsColumnSet { get; set; } + /// Gets or sets a value indicating whether is column set. + public bool IsColumnSet { get; set; } - /// Gets or sets a value indicating whether is row guid. - public bool IsRowGuid { get; set; } - } + /// Gets or sets a value indicating whether is row guid. + public bool IsRowGuid { get; set; } } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlDatabase.cs b/src/QuickCompareModel/DatabaseSchema/SqlDatabase.cs index 28c66c3..435db35 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlDatabase.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlDatabase.cs @@ -2,835 +2,831 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using QuickCompareModel.Models; + +/// +/// Class for running database queries and building lists that detail the content of the database schema. +/// +/// +/// Initialises a new instance of the class with a connection string and setting options. +/// +/// The database connection string for the current instance being inspected. +/// Collection of configuration settings. +public partial class SqlDatabase(string connectionString, QuickCompareOptions options) { - using System; - using System.Collections.Generic; - using System.Data; - using System.Data.SqlClient; - using System.IO; - using System.Linq; - using System.Reflection; - using System.Text; - using System.Text.RegularExpressions; - using System.Threading.Tasks; + private readonly string connectionString = connectionString; + private readonly QuickCompareOptions options = options; /// - /// Class for running database queries and building lists that detail the content of the database schema. + /// Initialises a new instance of the class with a connection string. /// - public class SqlDatabase + /// The database connection string for the current instance being inspected. + public SqlDatabase(string connectionString) + : this(connectionString, new QuickCompareOptions()) { - private readonly string connectionString; - private readonly QuickCompareOptions options; - - /// - /// Initialises a new instance of the class with a connection string and setting options. - /// - /// The database connection string for the current instance being inspected. - /// Collection of configuration settings. - public SqlDatabase(string connectionString, QuickCompareOptions options) - { - this.connectionString = connectionString; - this.options = options; - } - - /// - /// Initialises a new instance of the class with a connection string. - /// - /// The database connection string for the current instance being inspected. - public SqlDatabase(string connectionString) - : this(connectionString, new QuickCompareOptions()) - { - } + } - /// Handler for when the status message changes. - public event EventHandler LoaderStatusChanged; + /// Handler for when the status message changes. + public event EventHandler LoaderStatusChanged; - /// - /// Gets a friendly name for the database instance, including both server name and database name. - /// - public string FriendlyName + /// + /// Gets a friendly name for the database instance, including both server name and database name. + /// + public string FriendlyName + { + get { - get - { - var builder = new SqlConnectionStringBuilder(this.connectionString); - return $"[{builder.DataSource}].[{builder.InitialCatalog}]"; - } + var builder = new SqlConnectionStringBuilder(this.connectionString); + return $"[{builder.DataSource}].[{builder.InitialCatalog}]"; } + } - /// Gets or sets a list of instances, indexed by table name. - public Dictionary Tables { get; set; } = new Dictionary(); + /// Gets or sets a list of instances, indexed by table name. + public Dictionary Tables { get; set; } = []; - /// Gets or sets a list of instances, indexed by name. - public Dictionary UserTypes { get; set; } = new Dictionary(); + /// Gets or sets a list of instances, indexed by name. + public Dictionary UserTypes { get; set; } = []; - /// Gets or sets a list of database views, indexed by view name. - public Dictionary Views { get; set; } = new Dictionary(); + /// Gets or sets a list of database views, indexed by view name. + public Dictionary Views { get; set; } = []; - /// Gets or sets a list of SQL synonyms, indexed by synonym name. - public Dictionary Synonyms { get; set; } = new Dictionary(); + /// Gets or sets a list of SQL synonyms, indexed by synonym name. + public Dictionary Synonyms { get; set; } = []; - /// Gets or sets a list of instances, indexed by routine name. - /// User routines include views, functions and stored procedures. - public Dictionary UserRoutines { get; set; } = new Dictionary(); + /// Gets or sets a list of instances, indexed by routine name. + /// User routines include views, functions and stored procedures. + public Dictionary UserRoutines { get; set; } = []; - /// Gets or sets a list of instances, for both roles and users. - public List Permissions { get; set; } = new List(); + /// Gets or sets a list of instances, for both roles and users. + public List Permissions { get; set; } = []; - /// Gets or sets a list of instances for the database itself. - public List ExtendedProperties { get; set; } = new List(); + /// Gets or sets a list of instances for the database itself. + public List ExtendedProperties { get; set; } = []; - /// - /// Helper method to return embedded SQL resource by filename. - /// - /// Name of the SQL file without the extension. - /// SQL query text. - public static string LoadQueryFromResource(string queryName) - { - var resourceName = $"{nameof(QuickCompareModel)}.{nameof(DatabaseSchema)}.Queries.{queryName}.sql"; - using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); - return stream != null ? StreamToString(stream) : string.Empty; - } + /// + /// Helper method to return embedded SQL resource by filename. + /// + /// Name of the SQL file without the extension. + /// SQL query text. + public static string LoadQueryFromResource(string queryName) + { + var resourceName = $"{nameof(QuickCompareModel)}.{nameof(DatabaseSchema)}.Queries.{queryName}.sql"; + using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); + return stream != null ? StreamToString(stream) : string.Empty; + } - /// - /// Populate the models based on the supplied connection string. - /// - /// A representing the asynchronous operation. - public async Task PopulateSchemaModelAsync() - { - this.RaiseStatusChanged("Connecting"); + /// + /// Populate the models based on the supplied connection string. + /// + /// A representing the asynchronous operation. + public async Task PopulateSchemaModelAsync() + { + this.RaiseStatusChanged("Connecting"); - await this.LoadFullyQualifiedTableNamesAsync(); - await Task.WhenAll(this.RequiredItemTasks()); - await Task.WhenAll(this.DependentItemTasks()); + await this.LoadFullyQualifiedTableNamesAsync(); + await Task.WhenAll(this.RequiredItemTasks()); + await Task.WhenAll(this.DependentItemTasks()); - this.RaiseStatusChanged("Done"); - } + this.RaiseStatusChanged("Done"); + } - /// - /// Raises the status changed event. - /// - /// Status message. - protected virtual void RaiseStatusChanged(string message) => - this.LoaderStatusChanged?.Invoke(this, new StatusChangedEventArgs(message)); + /// + /// Raises the status changed event. + /// + /// Status message. + protected virtual void RaiseStatusChanged(string message) => + this.LoaderStatusChanged?.Invoke(this, new StatusChangedEventArgs(message)); - private static string StreamToString(Stream stream) - { - using var reader = new StreamReader(stream); - return reader.ReadToEnd(); - } + private static string StreamToString(Stream stream) + { + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } - private static SqlIndex LoadIndex(SqlDataReader dr) + private static SqlIndex LoadIndex(SqlDataReader dr) + { + var index = new SqlIndex(); + string desc; + var i = 0; + while (i < dr.FieldCount) { - var index = new SqlIndex(); - string desc; - var i = 0; - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "index_name": - index.IndexName = dr.GetString(i); - break; - case "index_keys": - index.SetColumnsFromString(dr.GetString(i)); - break; - case "index_description": - desc = dr.GetString(i); - index.IsPrimaryKey = desc.Contains("primary key"); - index.Clustered = !desc.Contains("nonclustered"); - index.Unique = desc.Contains("unique"); - index.IsUniqueKey = desc.Contains("unique key"); - index.FileGroup = Regex.Match(desc, "located on (.*)$").Groups[1].Value; - break; - default: - break; - } - - i++; + case "index_name": + index.IndexName = dr.GetString(i); + break; + case "index_keys": + index.SetColumnsFromString(dr.GetString(i)); + break; + case "index_description": + desc = dr.GetString(i); + index.IsPrimaryKey = desc.Contains("primary key"); + index.Clustered = !desc.Contains("nonclustered"); + index.Unique = desc.Contains("unique"); + index.IsUniqueKey = desc.Contains("unique key"); + index.FileGroup = FileGroupRegex().Match(desc).Groups[1].Value; + break; + default: + break; } - return index; + i++; } - private static SqlRelation LoadRelation(SqlDataReader dr) + return index; + } + + private static SqlRelation LoadRelation(SqlDataReader dr) + { + var relation = new SqlRelation(); + var i = 0; + while (i < dr.FieldCount) { - var relation = new SqlRelation(); - var i = 0; - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "RELATION_NAME": - relation.RelationName = dr.GetString(i); - break; - case "CHILD_SCHEMA": - relation.ChildSchema = dr.GetString(i); - break; - case "CHILD_TABLE": - relation.ChildTable = dr.GetString(i); - break; - case "CHILD_COLUMNS": - relation.ChildColumns = dr.GetString(i); - break; - case "UNIQUE_CONSTRAINT_NAME": - relation.UniqueConstraintName = dr.GetString(i); - break; - case "PARENT_SCHEMA": - relation.ParentSchema = dr.GetString(i); - break; - case "PARENT_TABLE": - relation.ParentTable = dr.GetString(i); - break; - case "PARENT_COLUMNS": - relation.ParentColumns = dr.GetString(i); - break; - case "UPDATE_RULE": - relation.UpdateRule = dr.GetString(i); - break; - case "DELETE_RULE": - relation.DeleteRule = dr.GetString(i); - break; - default: - break; - } - - i++; + case "RELATION_NAME": + relation.RelationName = dr.GetString(i); + break; + case "CHILD_SCHEMA": + relation.ChildSchema = dr.GetString(i); + break; + case "CHILD_TABLE": + relation.ChildTable = dr.GetString(i); + break; + case "CHILD_COLUMNS": + relation.ChildColumns = dr.GetString(i); + break; + case "UNIQUE_CONSTRAINT_NAME": + relation.UniqueConstraintName = dr.GetString(i); + break; + case "PARENT_SCHEMA": + relation.ParentSchema = dr.GetString(i); + break; + case "PARENT_TABLE": + relation.ParentTable = dr.GetString(i); + break; + case "PARENT_COLUMNS": + relation.ParentColumns = dr.GetString(i); + break; + case "UPDATE_RULE": + relation.UpdateRule = dr.GetString(i); + break; + case "DELETE_RULE": + relation.DeleteRule = dr.GetString(i); + break; + default: + break; } - return relation; + i++; } - private static SqlColumnDetail LoadColumnDetail(SqlDataReader dr) + return relation; + } + + private static SqlColumnDetail LoadColumnDetail(SqlDataReader dr) + { + var i = 0; + var detail = new SqlColumnDetail(); + while (i < dr.FieldCount) { - var i = 0; - var detail = new SqlColumnDetail(); - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "TABLE_SCHEMA": - detail.TableSchema = dr.GetString(i); - break; - case "TABLE_NAME": - detail.TableName = dr.GetString(i); - break; - case "COLUMN_NAME": - detail.ColumnName = dr.GetString(i); - break; - case "ORDINAL_POSITION": - detail.OrdinalPosition = dr.GetInt32(i); - break; - case "COLUMN_DEFAULT": - detail.ColumnDefault = dr.GetNullableString(i); - break; - case "IS_NULLABLE": - detail.IsNullable = !dr.IsDBNull(i) && dr.GetString(i) == "YES"; - break; - case "DATA_TYPE": - detail.DataType = dr.GetString(i); - break; - case "CHARACTER_MAXIMUM_LENGTH": - detail.CharacterMaximumLength = dr.GetNullableInt32(i); - break; - case "CHARACTER_OCTET_LENGTH": - detail.CharacterOctetLength = dr.GetNullableInt32(i); - break; - case "NUMERIC_PRECISION": - detail.NumericPrecision = dr.GetNullableByte(i); - break; - case "NUMERIC_PRECISION_RADIX": - detail.NumericPrecisionRadix = dr.GetNullableInt16(i); - break; - case "NUMERIC_SCALE": - detail.NumericScale = dr.GetNullableInt32(i); - break; - case "DATETIME_PRECISION": - detail.DatetimePrecision = dr.GetNullableInt16(i); - break; - case "CHARACTER_SET_NAME": - detail.CharacterSetName = dr.GetNullableString(i); - break; - case "COLLATION_NAME": - detail.CollationName = dr.GetNullableString(i); - break; - case "DOMAIN_SCHEMA": - detail.DomainSchema = dr.GetNullableString(i); - break; - case "DOMAIN_NAME": - detail.DomainName = dr.GetNullableString(i); - break; - case "IS_FULL_TEXT_INDEXED": - detail.IsFullTextIndexed = dr.GetNullableInt32AsBoolean(i); - break; - case "IS_COMPUTED": - detail.IsComputed = dr.GetNullableInt32AsBoolean(i); - break; - case "IS_IDENTITY": - detail.IsIdentity = dr.GetNullableInt32AsBoolean(i); - break; - case "IDENTITY_SEED": - detail.IdentitySeed = dr.GetNullableDecimal(i); - break; - case "IDENTITY_INCREMENT": - detail.IdentityIncrement = dr.GetNullableDecimal(i); - break; - case "IS_SPARSE": - detail.IsSparse = dr.GetNullableInt32AsBoolean(i); - break; - case "IS_COLUMN_SET": - detail.IsColumnSet = dr.GetNullableInt32AsBoolean(i); - break; - case "IS_ROW_GUID": - detail.IsRowGuid = dr.GetNullableInt32AsBoolean(i); - break; - default: - break; - } - - i++; + case "TABLE_SCHEMA": + detail.TableSchema = dr.GetString(i); + break; + case "TABLE_NAME": + detail.TableName = dr.GetString(i); + break; + case "COLUMN_NAME": + detail.ColumnName = dr.GetString(i); + break; + case "ORDINAL_POSITION": + detail.OrdinalPosition = dr.GetInt32(i); + break; + case "COLUMN_DEFAULT": + detail.ColumnDefault = dr.GetNullableString(i); + break; + case "IS_NULLABLE": + detail.IsNullable = !dr.IsDBNull(i) && dr.GetString(i) == "YES"; + break; + case "DATA_TYPE": + detail.DataType = dr.GetString(i); + break; + case "CHARACTER_MAXIMUM_LENGTH": + detail.CharacterMaximumLength = dr.GetNullableInt32(i); + break; + case "CHARACTER_OCTET_LENGTH": + detail.CharacterOctetLength = dr.GetNullableInt32(i); + break; + case "NUMERIC_PRECISION": + detail.NumericPrecision = dr.GetNullableByte(i); + break; + case "NUMERIC_PRECISION_RADIX": + detail.NumericPrecisionRadix = dr.GetNullableInt16(i); + break; + case "NUMERIC_SCALE": + detail.NumericScale = dr.GetNullableInt32(i); + break; + case "DATETIME_PRECISION": + detail.DatetimePrecision = dr.GetNullableInt16(i); + break; + case "CHARACTER_SET_NAME": + detail.CharacterSetName = dr.GetNullableString(i); + break; + case "COLLATION_NAME": + detail.CollationName = dr.GetNullableString(i); + break; + case "DOMAIN_SCHEMA": + detail.DomainSchema = dr.GetNullableString(i); + break; + case "DOMAIN_NAME": + detail.DomainName = dr.GetNullableString(i); + break; + case "IS_FULL_TEXT_INDEXED": + detail.IsFullTextIndexed = dr.GetNullableInt32AsBoolean(i); + break; + case "IS_COMPUTED": + detail.IsComputed = dr.GetNullableInt32AsBoolean(i); + break; + case "IS_IDENTITY": + detail.IsIdentity = dr.GetNullableInt32AsBoolean(i); + break; + case "IDENTITY_SEED": + detail.IdentitySeed = dr.GetNullableDecimal(i); + break; + case "IDENTITY_INCREMENT": + detail.IdentityIncrement = dr.GetNullableDecimal(i); + break; + case "IS_SPARSE": + detail.IsSparse = dr.GetNullableInt32AsBoolean(i); + break; + case "IS_COLUMN_SET": + detail.IsColumnSet = dr.GetNullableInt32AsBoolean(i); + break; + case "IS_ROW_GUID": + detail.IsRowGuid = dr.GetNullableInt32AsBoolean(i); + break; + default: + break; } - return detail; + i++; } - private static SqlUserType LoadUserType(SqlDataReader dr) + return detail; + } + + private static SqlUserType LoadUserType(SqlDataReader dr) + { + var i = 0; + var userType = new SqlUserType(); + while (i < dr.FieldCount) { - var i = 0; - var userType = new SqlUserType(); - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "custom_type_name": - userType.CustomTypeName = dr.GetString(i); - break; - case "schema_name": - userType.SchemaName = dr.GetString(i); - break; - case "underlying_type_name": - userType.UnderlyingTypeName = dr.GetString(i); - break; - case "precision": - userType.Precision = dr.GetNullableInt32(i); - break; - case "scale": - userType.Scale = dr.GetNullableInt32(i); - break; - case "max_length": - userType.MaxLength = dr.GetNullableInt32(i); - break; - case "is_nullable": - userType.IsNullable = dr.GetInt32AsBoolean(i); - break; - case "collation_name": - userType.CollationName = dr.GetNullableString(i); - break; - case "is_assembly_type": - userType.IsAssemblyType = dr.GetInt32AsBoolean(i); - break; - default: - break; - } - - i++; + case "custom_type_name": + userType.CustomTypeName = dr.GetString(i); + break; + case "schema_name": + userType.SchemaName = dr.GetString(i); + break; + case "underlying_type_name": + userType.UnderlyingTypeName = dr.GetString(i); + break; + case "precision": + userType.Precision = dr.GetNullableInt32(i); + break; + case "scale": + userType.Scale = dr.GetNullableInt32(i); + break; + case "max_length": + userType.MaxLength = dr.GetNullableInt32(i); + break; + case "is_nullable": + userType.IsNullable = dr.GetInt32AsBoolean(i); + break; + case "collation_name": + userType.CollationName = dr.GetNullableString(i); + break; + case "is_assembly_type": + userType.IsAssemblyType = dr.GetInt32AsBoolean(i); + break; + default: + break; } - return userType; + i++; } - private static SqlPermission LoadPermission(SqlDataReader dr) + return userType; + } + + private static SqlPermission LoadPermission(SqlDataReader dr) + { + var i = 0; + var permission = new SqlPermission(); + while (i < dr.FieldCount) { - var i = 0; - var permission = new SqlPermission(); - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "USER_NAME": - permission.UserName = dr.GetString(i); - break; - case "ROLE_NAME": - permission.RoleName = dr.GetNullableString(i); - break; - case "PERMISSION_TYPE": - permission.PermissionType = dr.GetNullableString(i); - break; - case "PERMISSION_STATE": - permission.PermissionState = dr.GetNullableString(i); - break; - case "OBJECT_TYPE": - permission.ObjectType = dr.GetNullableString(i); - break; - case "OBJECT_NAME": - permission.ObjectName = dr.GetNullableString(i); - break; - case "COLUMN_NAME": - permission.ColumnName = dr.GetNullableString(i); - break; - case "OBJECT_SCHEMA": - permission.ObjectSchema = dr.GetNullableString(i); - break; - default: - break; - } - - i++; + case "USER_NAME": + permission.UserName = dr.GetString(i); + break; + case "ROLE_NAME": + permission.RoleName = dr.GetNullableString(i); + break; + case "PERMISSION_TYPE": + permission.PermissionType = dr.GetNullableString(i); + break; + case "PERMISSION_STATE": + permission.PermissionState = dr.GetNullableString(i); + break; + case "OBJECT_TYPE": + permission.ObjectType = dr.GetNullableString(i); + break; + case "OBJECT_NAME": + permission.ObjectName = dr.GetNullableString(i); + break; + case "COLUMN_NAME": + permission.ColumnName = dr.GetNullableString(i); + break; + case "OBJECT_SCHEMA": + permission.ObjectSchema = dr.GetNullableString(i); + break; + default: + break; } - return permission; + i++; } - private static SqlExtendedProperty LoadExtendedProperty(SqlDataReader dr) + return permission; + } + + private static SqlExtendedProperty LoadExtendedProperty(SqlDataReader dr) + { + var i = 0; + var property = new SqlExtendedProperty(); + while (i < dr.FieldCount) { - var i = 0; - var property = new SqlExtendedProperty(); - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "PROPERTY_TYPE": - property.PropertyType = dr.GetString(i); - break; - case "OBJECT_NAME": - property.ObjectName = dr.GetNullableString(i); - break; - case "OBJECT_SCHEMA": - property.ObjectSchema = dr.GetNullableString(i); - break; - case "COLUMN_NAME": - property.ColumnName = dr.GetNullableString(i); - break; - case "PROPERTY_NAME": - property.PropertyName = dr.GetString(i); - break; - case "PROPERTY_VALUE": - property.PropertyValue = dr.GetNullableString(i); - break; - case "INDEX_NAME": - property.IndexName = dr.GetNullableString(i); - break; - case "TABLE_NAME": - property.TableName = dr.GetNullableString(i); - break; - case "TABLE_SCHEMA": - property.TableSchema = dr.GetNullableString(i); - break; - default: - break; - } - - i++; + case "PROPERTY_TYPE": + property.PropertyType = dr.GetString(i); + break; + case "OBJECT_NAME": + property.ObjectName = dr.GetNullableString(i); + break; + case "OBJECT_SCHEMA": + property.ObjectSchema = dr.GetNullableString(i); + break; + case "COLUMN_NAME": + property.ColumnName = dr.GetNullableString(i); + break; + case "PROPERTY_NAME": + property.PropertyName = dr.GetString(i); + break; + case "PROPERTY_VALUE": + property.PropertyValue = dr.GetNullableString(i); + break; + case "INDEX_NAME": + property.IndexName = dr.GetNullableString(i); + break; + case "TABLE_NAME": + property.TableName = dr.GetNullableString(i); + break; + case "TABLE_SCHEMA": + property.TableSchema = dr.GetNullableString(i); + break; + default: + break; } - return property; + i++; } - private static SqlTrigger LoadTrigger(SqlDataReader dr) + return property; + } + + private static SqlTrigger LoadTrigger(SqlDataReader dr) + { + var i = 0; + var trigger = new SqlTrigger(); + while (i < dr.FieldCount) { - var i = 0; - var trigger = new SqlTrigger(); - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "TRIGGER_NAME": - trigger.TriggerName = dr.GetString(i); - break; - case "TRIGGER_OWNER": - trigger.TriggerOwner = dr.GetNullableString(i); - break; - case "TABLE_SCHEMA": - trigger.TableSchema = dr.GetNullableString(i); - break; - case "TABLE_NAME": - trigger.TableName = dr.GetNullableString(i); - break; - case "IS_UPDATE": - trigger.IsUpdate = dr.GetInt32AsBoolean(i); - break; - case "IS_DELETE": - trigger.IsDelete = dr.GetInt32AsBoolean(i); - break; - case "IS_INSERT": - trigger.IsInsert = dr.GetInt32AsBoolean(i); - break; - case "IS_AFTER": - trigger.IsAfter = dr.GetInt32AsBoolean(i); - break; - case "IS_INSTEAD_OF": - trigger.IsInsteadOf = dr.GetInt32AsBoolean(i); - break; - case "IS_DISABLED": - trigger.IsDisabled = dr.GetInt32AsBoolean(i); - break; - case "TRIGGER_CONTENT": - trigger.TriggerContent = dr.GetString(i); - break; - default: - break; - } - - i++; + case "TRIGGER_NAME": + trigger.TriggerName = dr.GetString(i); + break; + case "TRIGGER_OWNER": + trigger.TriggerOwner = dr.GetNullableString(i); + break; + case "TABLE_SCHEMA": + trigger.TableSchema = dr.GetNullableString(i); + break; + case "TABLE_NAME": + trigger.TableName = dr.GetNullableString(i); + break; + case "IS_UPDATE": + trigger.IsUpdate = dr.GetInt32AsBoolean(i); + break; + case "IS_DELETE": + trigger.IsDelete = dr.GetInt32AsBoolean(i); + break; + case "IS_INSERT": + trigger.IsInsert = dr.GetInt32AsBoolean(i); + break; + case "IS_AFTER": + trigger.IsAfter = dr.GetInt32AsBoolean(i); + break; + case "IS_INSTEAD_OF": + trigger.IsInsteadOf = dr.GetInt32AsBoolean(i); + break; + case "IS_DISABLED": + trigger.IsDisabled = dr.GetInt32AsBoolean(i); + break; + case "TRIGGER_CONTENT": + trigger.TriggerContent = dr.GetString(i); + break; + default: + break; } - return trigger; + i++; } - private Task[] RequiredItemTasks() - { - var tasks = new List() { this.LoadRelationsAsync(), this.LoadColumnDetailsAsync() }; + return trigger; + } - if (this.options.CompareUserTypes) - { - tasks.Add(this.LoadUserTypesAsync()); - } + [GeneratedRegex("located on (.*)$")] private static partial Regex FileGroupRegex(); - if (this.options.ComparePermissions) - { - tasks.AddRange(new Task[] { this.LoadRolePermissionsAsync(), this.LoadUserPermissionsAsync() }); - } + private Task[] RequiredItemTasks() + { + var tasks = new List() { this.LoadRelationsAsync(), this.LoadColumnDetailsAsync() }; - if (this.options.CompareProperties) - { - tasks.Add(this.LoadExtendedPropertiesAsync()); - } + if (this.options.CompareUserTypes) + { + tasks.Add(this.LoadUserTypesAsync()); + } - if (this.options.CompareTriggers) - { - tasks.Add(this.LoadTriggersAsync()); - } + if (this.options.ComparePermissions) + { + tasks.AddRange(new Task[] { this.LoadRolePermissionsAsync(), this.LoadUserPermissionsAsync() }); + } - if (this.options.CompareSynonyms) - { - tasks.Add(this.LoadSynonymsAsync()); - } + if (this.options.CompareProperties) + { + tasks.Add(this.LoadExtendedPropertiesAsync()); + } - if (this.options.CompareObjects) - { - tasks.AddRange(new Task[] { this.LoadViewsAsync(), this.LoadUserRoutinesAsync(), this.LoadUserRoutineDefinitionsAsync() }); - } + if (this.options.CompareTriggers) + { + tasks.Add(this.LoadTriggersAsync()); + } - if (this.options.CompareIndexes) - { - foreach (var fullyQualifiedTableName in this.Tables.Keys) - { - tasks.Add(this.LoadIndexesAsync(fullyQualifiedTableName)); - } - } + if (this.options.CompareSynonyms) + { + tasks.Add(this.LoadSynonymsAsync()); + } - return tasks.ToArray(); + if (this.options.CompareObjects) + { + tasks.AddRange(new Task[] { this.LoadViewsAsync(), this.LoadUserRoutinesAsync(), this.LoadUserRoutineDefinitionsAsync() }); } - private Task[] DependentItemTasks() + if (this.options.CompareIndexes) { - var tasks = new List(); - if (this.options.CompareObjects) + foreach (var fullyQualifiedTableName in this.Tables.Keys) { - tasks.Add(this.LoadUserRoutineDefinitionsAsync()); + tasks.Add(this.LoadIndexesAsync(fullyQualifiedTableName)); } + } + + return [.. tasks]; + } + + private Task[] DependentItemTasks() + { + var tasks = new List(); + if (this.options.CompareObjects) + { + tasks.Add(this.LoadUserRoutineDefinitionsAsync()); + } - if (this.options.CompareIndexes) + if (this.options.CompareIndexes) + { + foreach (var fullyQualifiedTableName in this.Tables.Keys) { - foreach (var fullyQualifiedTableName in this.Tables.Keys) + foreach (var index in this.Tables[fullyQualifiedTableName].Indexes) { - foreach (var index in this.Tables[fullyQualifiedTableName].Indexes) - { - tasks.Add(this.LoadIncludedColumnsForIndexAsync(index)); - } + tasks.Add(this.LoadIncludedColumnsForIndexAsync(index)); } } - - return tasks.ToArray(); } - private async Task LoadFullyQualifiedTableNamesAsync() + return tasks.ToArray(); + } + + private async Task LoadFullyQualifiedTableNamesAsync() + { + this.RaiseStatusChanged("Reading tables"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("TableNames"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading tables"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("TableNames"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) - { - this.Tables.Add($"[{dr.GetString(0)}].[{dr.GetString(1)}]", new SqlTable()); - } + this.Tables.Add($"[{dr.GetString(0)}].[{dr.GetString(1)}]", new SqlTable()); } + } - private async Task LoadIndexesAsync(string fullyQualifiedTableName) + private async Task LoadIndexesAsync(string fullyQualifiedTableName) + { + this.RaiseStatusChanged($"Reading indexes for table {fullyQualifiedTableName}"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand("sp_helpindex", connection); + command.CommandType = CommandType.StoredProcedure; + command.Parameters.AddWithValue("@objname", fullyQualifiedTableName); + + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged($"Reading indexes for table {fullyQualifiedTableName}"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand("sp_helpindex", connection); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue("@objname", fullyQualifiedTableName); + var index = LoadIndex(dr); + index.TableSchema = fullyQualifiedTableName.GetSchemaName(); + index.TableName = fullyQualifiedTableName.GetObjectName(); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) - { - var index = LoadIndex(dr); - index.TableSchema = fullyQualifiedTableName.GetSchemaName(); - index.TableName = fullyQualifiedTableName.GetObjectName(); + this.Tables[fullyQualifiedTableName].Indexes.Add(index); + } + } - this.Tables[fullyQualifiedTableName].Indexes.Add(index); + private async Task LoadIncludedColumnsForIndexAsync(SqlIndex index) + { + this.RaiseStatusChanged($"Reading index included columns for {index.IndexName}"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("IncludedColumnsForIndex"), connection); + command.Parameters.AddWithValue("@TableName", index.TableName); + command.Parameters.AddWithValue("@IndexName", index.IndexName); + command.Parameters.AddWithValue("@TableSchema", index.TableSchema); + + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) + { + if (dr.GetBoolean(3)) + { + index.IncludedColumns.Add(dr.GetString(1), dr.GetBoolean(2)); } } + } - private async Task LoadIncludedColumnsForIndexAsync(SqlIndex index) + private async Task LoadRelationsAsync() + { + this.RaiseStatusChanged("Reading relations"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("Relations"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged($"Reading index included columns for {index.IndexName}"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("IncludedColumnsForIndex"), connection); - command.Parameters.AddWithValue("@TableName", index.TableName); - command.Parameters.AddWithValue("@IndexName", index.IndexName); - command.Parameters.AddWithValue("@TableSchema", index.TableSchema); + var relation = LoadRelation(dr); + var fullyQualifiedChildTable = relation.ChildTable.PrependSchemaName(relation.ChildSchema); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) + if (this.Tables.TryGetValue(fullyQualifiedChildTable, out var table)) { - if (dr.GetBoolean(3)) - { - index.IncludedColumns.Add(dr.GetString(1), dr.GetBoolean(2)); - } + table.Relations.Add(relation); } } + } - private async Task LoadRelationsAsync() + private async Task LoadColumnDetailsAsync() + { + this.RaiseStatusChanged("Reading column details"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("ColumnDetails"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading relations"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("Relations"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) - { - var relation = LoadRelation(dr); - var fullyQualifiedChildTable = relation.ChildTable.PrependSchemaName(relation.ChildSchema); + var detail = LoadColumnDetail(dr); + var fullyQualifiedTableName = detail.TableName.PrependSchemaName(detail.TableSchema); - if (this.Tables.TryGetValue(fullyQualifiedChildTable, out var table)) - { - table.Relations.Add(relation); - } + if (this.Tables.TryGetValue(fullyQualifiedTableName, out var table)) + { + table.ColumnDetails.Add(detail); } } + } - private async Task LoadColumnDetailsAsync() + private async Task LoadUserTypesAsync() + { + this.RaiseStatusChanged("Reading user types"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("UserTypes"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading column details"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("ColumnDetails"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) - { - var detail = LoadColumnDetail(dr); - var fullyQualifiedTableName = detail.TableName.PrependSchemaName(detail.TableSchema); - - if (this.Tables.TryGetValue(fullyQualifiedTableName, out var table)) - { - table.ColumnDetails.Add(detail); - } - } + var userType = LoadUserType(dr); + this.UserTypes.Add(userType.CustomTypeName.PrependSchemaName(userType.SchemaName), userType); } + } - private async Task LoadUserTypesAsync() + private async Task LoadRolePermissionsAsync() + { + this.RaiseStatusChanged("Reading role permissions"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("RolePermissions"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading user types"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("UserTypes"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) - { - var userType = LoadUserType(dr); - this.UserTypes.Add(userType.CustomTypeName.PrependSchemaName(userType.SchemaName), userType); - } + this.Permissions.Add(LoadPermission(dr)); } + } - private async Task LoadRolePermissionsAsync() + private async Task LoadUserPermissionsAsync() + { + this.RaiseStatusChanged("Reading user permissions"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("UserPermissions"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading role permissions"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("RolePermissions"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) - { - this.Permissions.Add(LoadPermission(dr)); - } + this.Permissions.Add(LoadPermission(dr)); } + } - private async Task LoadUserPermissionsAsync() + private async Task LoadExtendedPropertiesAsync() + { + this.RaiseStatusChanged("Reading extended properties"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("ExtendedProperties"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading user permissions"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("UserPermissions"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) - { - this.Permissions.Add(LoadPermission(dr)); - } + this.ExtendedProperties.Add(LoadExtendedProperty(dr)); } + } - private async Task LoadExtendedPropertiesAsync() + private async Task LoadTriggersAsync() + { + this.RaiseStatusChanged("Reading triggers"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("Triggers"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading extended properties"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("ExtendedProperties"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) + var trigger = LoadTrigger(dr); + var fullyQualifiedTableName = trigger.TableName.PrependSchemaName(trigger.TableSchema); + + if (this.Tables.TryGetValue(fullyQualifiedTableName, out var table)) { - this.ExtendedProperties.Add(LoadExtendedProperty(dr)); + table.Triggers.Add(trigger); } } + } - private async Task LoadTriggersAsync() + private async Task LoadSynonymsAsync() + { + this.RaiseStatusChanged("Reading synonyms"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("Synonyms"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading triggers"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("Triggers"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) + var i = 0; + var name = string.Empty; + var def = string.Empty; + while (i < dr.FieldCount) { - var trigger = LoadTrigger(dr); - var fullyQualifiedTableName = trigger.TableName.PrependSchemaName(trigger.TableSchema); - - if (this.Tables.TryGetValue(fullyQualifiedTableName, out var table)) + switch (dr.GetName(i)) { - table.Triggers.Add(trigger); + case "SYNONYM_NAME": + name = dr.GetString(i); + break; + case "BASE_OBJECT_NAME": + def = dr.GetString(i); + break; } + + i++; } + + this.Synonyms.Add(name, def); } + } - private async Task LoadSynonymsAsync() + private async Task LoadViewsAsync() + { + this.RaiseStatusChanged("Reading views"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("Views"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading synonyms"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("Synonyms"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) + var i = 0; + var name = string.Empty; + var schema = string.Empty; + var def = string.Empty; + while (i < dr.FieldCount) { - var i = 0; - var name = string.Empty; - var def = string.Empty; - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "SYNONYM_NAME": - name = dr.GetString(i); - break; - case "BASE_OBJECT_NAME": - def = dr.GetString(i); - break; - } - - i++; + case "VIEW_NAME": + name = dr.GetString(i); + break; + case "TABLE_SCHEMA": + schema = dr.GetString(i); + break; + case "VIEW_DEFINITION": + def = dr.GetString(i); + break; } - this.Synonyms.Add(name, def); + i++; } + + this.Views.Add(name.PrependSchemaName(schema), def); } + } - private async Task LoadViewsAsync() + private async Task LoadUserRoutinesAsync() + { + this.RaiseStatusChanged("Reading user routines"); + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("UserRoutines"), connection); + await connection.OpenAsync(); + using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); + while (await dr.ReadAsync()) { - this.RaiseStatusChanged("Reading views"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("Views"), connection); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) + var routine = new SqlUserRoutine(); + var name = string.Empty; + var schema = string.Empty; + var i = 0; + while (i < dr.FieldCount) { - var i = 0; - var name = string.Empty; - var schema = string.Empty; - var def = string.Empty; - while (i < dr.FieldCount) + switch (dr.GetName(i)) { - switch (dr.GetName(i)) - { - case "VIEW_NAME": - name = dr.GetString(i); - break; - case "TABLE_SCHEMA": - schema = dr.GetString(i); - break; - case "VIEW_DEFINITION": - def = dr.GetString(i); - break; - } - - i++; + case "ROUTINE_NAME": + name = dr.GetString(i); + break; + case "ROUTINE_SCHEMA": + schema = dr.GetString(i); + break; + case "ROUTINE_TYPE": + routine.RoutineType = dr.GetString(i); + break; + default: + break; } - this.Views.Add(name.PrependSchemaName(schema), def); + i++; } + + this.UserRoutines.Add(name.PrependSchemaName(schema), routine); } + } - private async Task LoadUserRoutinesAsync() + private async Task LoadUserRoutineDefinitionsAsync() + { + using var connection = new SqlConnection(this.connectionString); + using var command = new SqlCommand(LoadQueryFromResource("UserRoutineDefinitions"), connection); + command.Parameters.Add("@routinename", SqlDbType.VarChar, 128); + var sb = new StringBuilder(); + foreach (var routine in this.UserRoutines.Keys) { - this.RaiseStatusChanged("Reading user routines"); - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("UserRoutines"), connection); + this.RaiseStatusChanged($"Reading routine definition {Array.IndexOf([.. this.UserRoutines.Keys], routine) + 1} of {this.UserRoutines.Count}"); + + command.Parameters["@routinename"].Value = routine.GetObjectName(); await connection.OpenAsync(); using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); while (await dr.ReadAsync()) { - var routine = new SqlUserRoutine(); - var name = string.Empty; - var schema = string.Empty; - var i = 0; - while (i < dr.FieldCount) - { - switch (dr.GetName(i)) - { - case "ROUTINE_NAME": - name = dr.GetString(i); - break; - case "ROUTINE_SCHEMA": - schema = dr.GetString(i); - break; - case "ROUTINE_TYPE": - routine.RoutineType = dr.GetString(i); - break; - default: - break; - } - - i++; - } - - this.UserRoutines.Add(name.PrependSchemaName(schema), routine); + sb.Append(dr.GetString(0)); } - } - private async Task LoadUserRoutineDefinitionsAsync() - { - using var connection = new SqlConnection(this.connectionString); - using var command = new SqlCommand(LoadQueryFromResource("UserRoutineDefinitions"), connection); - command.Parameters.Add("@routinename", SqlDbType.VarChar, 128); - var sb = new StringBuilder(); - foreach (var routine in this.UserRoutines.Keys) - { - this.RaiseStatusChanged($"Reading routine definition {Array.IndexOf(this.UserRoutines.Keys.ToArray(), routine) + 1} of {this.UserRoutines.Count}"); - - command.Parameters["@routinename"].Value = routine.GetObjectName(); - await connection.OpenAsync(); - using var dr = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection); - while (await dr.ReadAsync()) - { - sb.Append(dr.GetString(0)); - } - - this.UserRoutines[routine].RoutineDefinition = sb.ToString(); - sb.Clear(); - } + this.UserRoutines[routine].RoutineDefinition = sb.ToString(); + sb.Clear(); } } } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlExtendedProperty.cs b/src/QuickCompareModel/DatabaseSchema/SqlExtendedProperty.cs index 82c8708..a5ebb52 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlExtendedProperty.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlExtendedProperty.cs @@ -2,56 +2,55 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +using QuickCompareModel.DatabaseSchema.Enums; + +/// +/// Class representing SQL extended property. +/// +public class SqlExtendedProperty { - using QuickCompareModel.DatabaseSchema.Enums; - - /// - /// Class representing SQL extended property. - /// - public class SqlExtendedProperty - { - /// Gets or sets the property type. - public string PropertyType { get; set; } - - /// Gets or sets the object name. - public string ObjectName { get; set; } - - /// Gets or sets the object schema. - public string ObjectSchema { get; set; } - - /// Gets or sets the column name. - public string ColumnName { get; set; } - - /// Gets or sets the property name. - public string PropertyName { get; set; } - - /// Gets or sets the property value. - public string PropertyValue { get; set; } - - /// Gets or sets the table name. - public string TableName { get; set; } - - /// Gets or sets the index name. - public string IndexName { get; set; } - - /// Gets or sets the table schema. - public string TableSchema { get; set; } - - /// Gets the full ID. - public string FullId => !string.IsNullOrEmpty(this.ObjectName) - ? string.IsNullOrEmpty(this.ColumnName) - ? $"[{this.ObjectName}].[{this.PropertyName}].[{this.Type}]" - : $"[{this.ObjectName}].[{this.PropertyName}].[{this.ColumnName}].[{this.Type}]" - : this.PropertyName; - - /// Gets the target . - public PropertyObjectType Type => this.PropertyType != "INDEX" - ? !string.IsNullOrEmpty(this.TableName) - ? string.IsNullOrEmpty(this.ColumnName) ? PropertyObjectType.Table : PropertyObjectType.TableColumn - : this.PropertyType != "DATABASE" - ? string.IsNullOrEmpty(this.ColumnName) ? PropertyObjectType.Routine : PropertyObjectType.RoutineColumn - : PropertyObjectType.Database - : PropertyObjectType.Index; - } + /// Gets or sets the property type. + public string PropertyType { get; set; } + + /// Gets or sets the object name. + public string ObjectName { get; set; } + + /// Gets or sets the object schema. + public string ObjectSchema { get; set; } + + /// Gets or sets the column name. + public string ColumnName { get; set; } + + /// Gets or sets the property name. + public string PropertyName { get; set; } + + /// Gets or sets the property value. + public string PropertyValue { get; set; } + + /// Gets or sets the table name. + public string TableName { get; set; } + + /// Gets or sets the index name. + public string IndexName { get; set; } + + /// Gets or sets the table schema. + public string TableSchema { get; set; } + + /// Gets the full ID. + public string FullId => !string.IsNullOrEmpty(this.ObjectName) + ? string.IsNullOrEmpty(this.ColumnName) + ? $"[{this.ObjectName}].[{this.PropertyName}].[{this.Type}]" + : $"[{this.ObjectName}].[{this.PropertyName}].[{this.ColumnName}].[{this.Type}]" + : this.PropertyName; + + /// Gets the target . + public PropertyObjectType Type => this.PropertyType != "INDEX" + ? !string.IsNullOrEmpty(this.TableName) + ? string.IsNullOrEmpty(this.ColumnName) ? PropertyObjectType.Table : PropertyObjectType.TableColumn + : this.PropertyType != "DATABASE" + ? string.IsNullOrEmpty(this.ColumnName) ? PropertyObjectType.Routine : PropertyObjectType.RoutineColumn + : PropertyObjectType.Database + : PropertyObjectType.Index; } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlIndex.cs b/src/QuickCompareModel/DatabaseSchema/SqlIndex.cs index b6ba5d6..a9239ca 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlIndex.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlIndex.cs @@ -2,93 +2,92 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema -{ - using System.Collections.Generic; - using System.Text; +namespace QuickCompareModel.DatabaseSchema; - /// - /// Class to represent an index in the database. - /// - public class SqlIndex - { - /// Gets or sets a value indicating whether is primary key. - public bool IsPrimaryKey { get; set; } +using System.Collections.Generic; +using System.Text; - /// Gets or sets the table schema. - public string TableSchema { get; set; } +/// +/// Class to represent an index in the database. +/// +public class SqlIndex +{ + /// Gets or sets a value indicating whether is primary key. + public bool IsPrimaryKey { get; set; } - /// Gets or sets the table name. - public string TableName { get; set; } + /// Gets or sets the table schema. + public string TableSchema { get; set; } - /// Gets or sets the index name. - public string IndexName { get; set; } + /// Gets or sets the table name. + public string TableName { get; set; } - /// Gets or sets a value indicating whether is clustered. - public bool Clustered { get; set; } + /// Gets or sets the index name. + public string IndexName { get; set; } - /// Gets or sets a value indicating whether is unique. - public bool Unique { get; set; } + /// Gets or sets a value indicating whether is clustered. + public bool Clustered { get; set; } - /// Gets or sets a value indicating whether is unique key. - public bool IsUniqueKey { get; set; } + /// Gets or sets a value indicating whether is unique. + public bool Unique { get; set; } - /// Gets or sets the index columns. - public Dictionary> IndexColumns { get; set; } + /// Gets or sets a value indicating whether is unique key. + public bool IsUniqueKey { get; set; } - /// Gets or sets the file group. - public string FileGroup { get; set; } + /// Gets or sets the index columns. + public Dictionary> IndexColumns { get; set; } - /// Gets or sets the columns. - public Dictionary Columns { get; set; } = new Dictionary(); + /// Gets or sets the file group. + public string FileGroup { get; set; } - /// Gets or sets the included columns. - public Dictionary IncludedColumns { get; set; } = new Dictionary(); + /// Gets or sets the columns. + public Dictionary Columns { get; set; } = []; - /// Gets the full ID. - public string FullId => $"[{this.TableSchema}].[{this.TableName}].[{this.IndexName}]"; + /// Gets or sets the included columns. + public Dictionary IncludedColumns { get; set; } = []; - /// Gets the columns as a string. - public string ColumnsToString => FlagListToString(this.Columns); + /// Gets the full ID. + public string FullId => $"[{this.TableSchema}].[{this.TableName}].[{this.IndexName}]"; - /// Gets the included columns as a string. - public string IncludedColumnsToString => FlagListToString(this.IncludedColumns); + /// Gets the columns as a string. + public string ColumnsToString => FlagListToString(this.Columns); - /// Gets the item type. - public string ItemType => !this.IsPrimaryKey ? this.IsUniqueKey ? "Unique key" : "Index" : "Primary key"; + /// Gets the included columns as a string. + public string IncludedColumnsToString => FlagListToString(this.IncludedColumns); - /// Sets the columns value from a given string. - /// Comma-separated list of column names. - public void SetColumnsFromString(string value) - { - var columnNames = value.Split(','); - foreach (var columnName in columnNames) - { - if (columnName.Contains("(-)")) - { - this.Columns.Add(columnName.Replace("(-)", string.Empty).Trim(), false); - } - else - { - this.Columns.Add(columnName.Trim(), true); - } - } - } + /// Gets the item type. + public string ItemType => !this.IsPrimaryKey ? this.IsUniqueKey ? "Unique key" : "Index" : "Primary key"; - private static string FlagListToString(Dictionary flagList) + /// Sets the columns value from a given string. + /// Comma-separated list of column names. + public void SetColumnsFromString(string value) + { + var columnNames = value.Split(','); + foreach (var columnName in columnNames) { - if (flagList == null) + if (columnName.Contains("(-)")) { - return string.Empty; + this.Columns.Add(columnName.Replace("(-)", string.Empty).Trim(), false); } - - var sb = new StringBuilder(); - foreach (var pair in flagList) + else { - sb.AppendLine($"{pair.Key}, {pair.Value}"); + this.Columns.Add(columnName.Trim(), true); } + } + } + + private static string FlagListToString(Dictionary flagList) + { + if (flagList == null) + { + return string.Empty; + } - return sb.ToString(); + var sb = new StringBuilder(); + foreach (var pair in flagList) + { + sb.AppendLine($"{pair.Key}, {pair.Value}"); } + + return sb.ToString(); } } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlPermission.cs b/src/QuickCompareModel/DatabaseSchema/SqlPermission.cs index 04de8a2..614093d 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlPermission.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlPermission.cs @@ -2,65 +2,64 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema -{ - using QuickCompareModel.DatabaseSchema.Enums; +namespace QuickCompareModel.DatabaseSchema; - /// - /// Class to represent a permission in the database. - /// - public class SqlPermission - { - /// Gets or sets the role name. - public string RoleName { get; set; } +using QuickCompareModel.DatabaseSchema.Enums; - /// Gets or sets the user name. - public string UserName { get; set; } +/// +/// Class to represent a permission in the database. +/// +public class SqlPermission +{ + /// Gets or sets the role name. + public string RoleName { get; set; } - /// Gets or sets the permission type. - public string PermissionType { get; set; } + /// Gets or sets the user name. + public string UserName { get; set; } - /// Gets or sets the permission state. - public string PermissionState { get; set; } + /// Gets or sets the permission type. + public string PermissionType { get; set; } - /// Gets or sets the object type. - public string ObjectType { get; set; } + /// Gets or sets the permission state. + public string PermissionState { get; set; } - /// Gets or sets the object name. - public string ObjectName { get; set; } + /// Gets or sets the object type. + public string ObjectType { get; set; } - /// Gets or sets the column name. - public string ColumnName { get; set; } + /// Gets or sets the object name. + public string ObjectName { get; set; } - /// Gets or sets the object schema. - public string ObjectSchema { get; set; } + /// Gets or sets the column name. + public string ColumnName { get; set; } - /// Gets the full ID. - public string FullId => $"[{this.RoleName}].[{this.UserName}].[{this.PermissionType}].[{this.PermissionState}].[{this.ObjectType}].[{this.ObjectName}].[{this.ColumnName}]"; + /// Gets or sets the object schema. + public string ObjectSchema { get; set; } - /// Gets the type. - public PermissionObjectType Type => this.ObjectType switch - { - "SQL_STORED_PROCEDURE" => PermissionObjectType.SqlStoredProcedure, - "USER_TABLE" => PermissionObjectType.UserTable, - "SYSTEM_TABLE" => PermissionObjectType.SystemTable, - "SYNONYM" => PermissionObjectType.Synonym, - "VIEW" => PermissionObjectType.View, - "SQL_SCALAR_FUNCTION" => PermissionObjectType.SqlFunction, - "SQL_TABLE_VALUED_FUNCTION" => PermissionObjectType.SqlFunction, - "SQL_INLINE_TABLE_VALUED_FUNCTION" => PermissionObjectType.SqlFunction, - "DATABASE" => PermissionObjectType.Database, - _ => PermissionObjectType.Unknown, - }; + /// Gets the full ID. + public string FullId => $"[{this.RoleName}].[{this.UserName}].[{this.PermissionType}].[{this.PermissionState}].[{this.ObjectType}].[{this.ObjectName}].[{this.ColumnName}]"; + + /// Gets the type. + public PermissionObjectType Type => this.ObjectType switch + { + "SQL_STORED_PROCEDURE" => PermissionObjectType.SqlStoredProcedure, + "USER_TABLE" => PermissionObjectType.UserTable, + "SYSTEM_TABLE" => PermissionObjectType.SystemTable, + "SYNONYM" => PermissionObjectType.Synonym, + "VIEW" => PermissionObjectType.View, + "SQL_SCALAR_FUNCTION" => PermissionObjectType.SqlFunction, + "SQL_TABLE_VALUED_FUNCTION" => PermissionObjectType.SqlFunction, + "SQL_INLINE_TABLE_VALUED_FUNCTION" => PermissionObjectType.SqlFunction, + "DATABASE" => PermissionObjectType.Database, + _ => PermissionObjectType.Unknown, + }; - private string TargetName => string.IsNullOrEmpty(this.RoleName) ? this.UserName : this.RoleName; + private string TargetName => string.IsNullOrEmpty(this.RoleName) ? this.UserName : this.RoleName; - private string TargetType => string.IsNullOrEmpty(this.RoleName) ? "user" : "role"; + private string TargetType => string.IsNullOrEmpty(this.RoleName) ? "user" : "role"; - /// Generates a text description of the difference. - /// Description of the difference. - public override string ToString() => this.PermissionType == "REFERENCES" - ? $"REFERENCES column: [{this.ColumnName}] {(this.PermissionState == "GRANT" ? string.Empty : "DENIED ")}for {this.TargetType}: [{this.TargetName}]" - : $"[{this.PermissionType}] {(this.PermissionState == "GRANT" ? string.Empty : "DENIED ")}for {this.TargetType}: [{this.TargetName}]"; - } + /// Generates a text description of the difference. + /// Description of the difference. + public override string ToString() => this.PermissionType == "REFERENCES" + ? $"REFERENCES column: [{this.ColumnName}] {(this.PermissionState == "GRANT" ? string.Empty : "DENIED ")}for {this.TargetType}: [{this.TargetName}]" + : $"[{this.PermissionType}] {(this.PermissionState == "GRANT" ? string.Empty : "DENIED ")}for {this.TargetType}: [{this.TargetName}]"; } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlRelation.cs b/src/QuickCompareModel/DatabaseSchema/SqlRelation.cs index 1f529fe..3b9fc48 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlRelation.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlRelation.cs @@ -2,41 +2,40 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +/// +/// Class to represent a relationship in the database. +/// +public class SqlRelation { - /// - /// Class to represent a relationship in the database. - /// - public class SqlRelation - { - /// Gets or sets the relation nName. - public string RelationName { get; set; } + /// Gets or sets the relation nName. + public string RelationName { get; set; } - /// Gets or sets the child schema. - public string ChildSchema { get; set; } + /// Gets or sets the child schema. + public string ChildSchema { get; set; } - /// Gets or sets the child table. - public string ChildTable { get; set; } + /// Gets or sets the child table. + public string ChildTable { get; set; } - /// Gets or sets the child columns. - public string ChildColumns { get; set; } + /// Gets or sets the child columns. + public string ChildColumns { get; set; } - /// Gets or sets the unique-constraint name. - public string UniqueConstraintName { get; set; } + /// Gets or sets the unique-constraint name. + public string UniqueConstraintName { get; set; } - /// Gets or sets the parent schema. - public string ParentSchema { get; set; } + /// Gets or sets the parent schema. + public string ParentSchema { get; set; } - /// Gets or sets the parent table. - public string ParentTable { get; set; } + /// Gets or sets the parent table. + public string ParentTable { get; set; } - /// Gets or sets the parent columns. - public string ParentColumns { get; set; } + /// Gets or sets the parent columns. + public string ParentColumns { get; set; } - /// Gets or sets the update rule. - public string UpdateRule { get; set; } + /// Gets or sets the update rule. + public string UpdateRule { get; set; } - /// Gets or sets the delete rule. - public string DeleteRule { get; set; } - } + /// Gets or sets the delete rule. + public string DeleteRule { get; set; } } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlTable.cs b/src/QuickCompareModel/DatabaseSchema/SqlTable.cs index 6d5a44b..9257a64 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlTable.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlTable.cs @@ -2,31 +2,30 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema -{ - using System.Collections.Generic; +namespace QuickCompareModel.DatabaseSchema; + +using System.Collections.Generic; - /// - /// Class to represent a table in the database. - /// - public class SqlTable - { - /// Gets or sets the column details. - public List ColumnDetails { get; set; } = new List(); +/// +/// Class to represent a table in the database. +/// +public class SqlTable +{ + /// Gets or sets the column details. + public List ColumnDetails { get; set; } = []; - /// Gets or sets the relations. - public List Relations { get; set; } = new List(); + /// Gets or sets the relations. + public List Relations { get; set; } = []; - /// Gets or sets the indexes. - public List Indexes { get; set; } = new List(); + /// Gets or sets the indexes. + public List Indexes { get; set; } = []; - /// Gets or sets the triggers. - public List Triggers { get; set; } = new List(); + /// Gets or sets the triggers. + public List Triggers { get; set; } = []; - /// Gets a value indicating whether column has unique index. - /// Name of the column. - /// True if has a unique index. - public bool ColumnHasUniqueIndex(string columnName) => - this.Indexes.Exists(x => x.Unique && x.Columns.ContainsKey(columnName) && x.Columns.Count == 1); - } + /// Gets a value indicating whether column has unique index. + /// Name of the column. + /// True if has a unique index. + public bool ColumnHasUniqueIndex(string columnName) => + this.Indexes.Exists(x => x.Unique && x.Columns.ContainsKey(columnName) && x.Columns.Count == 1); } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlTrigger.cs b/src/QuickCompareModel/DatabaseSchema/SqlTrigger.cs index a4c6b81..55022b9 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlTrigger.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlTrigger.cs @@ -2,47 +2,46 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +/// +/// Class to represent a trigger in the database. +/// +public class SqlTrigger { - /// - /// Class to represent a trigger in the database. - /// - public class SqlTrigger - { - /// Gets or sets the file group. - public string FileGroup { get; set; } + /// Gets or sets the file group. + public string FileGroup { get; set; } - /// Gets or sets the trigger name. - public string TriggerName { get; set; } + /// Gets or sets the trigger name. + public string TriggerName { get; set; } - /// Gets or sets the trigger owner. - public string TriggerOwner { get; set; } + /// Gets or sets the trigger owner. + public string TriggerOwner { get; set; } - /// Gets or sets the table schema. - public string TableSchema { get; set; } + /// Gets or sets the table schema. + public string TableSchema { get; set; } - /// Gets or sets the table name. - public string TableName { get; set; } + /// Gets or sets the table name. + public string TableName { get; set; } - /// Gets or sets a value indicating whether is update. - public bool IsUpdate { get; set; } + /// Gets or sets a value indicating whether is update. + public bool IsUpdate { get; set; } - /// Gets or sets a value indicating whether is delete. - public bool IsDelete { get; set; } + /// Gets or sets a value indicating whether is delete. + public bool IsDelete { get; set; } - /// Gets or sets a value indicating whether is insert. - public bool IsInsert { get; set; } + /// Gets or sets a value indicating whether is insert. + public bool IsInsert { get; set; } - /// Gets or sets a value indicating whether is after. - public bool IsAfter { get; set; } + /// Gets or sets a value indicating whether is after. + public bool IsAfter { get; set; } - /// Gets or sets a value indicating whether is instead of. - public bool IsInsteadOf { get; set; } + /// Gets or sets a value indicating whether is instead of. + public bool IsInsteadOf { get; set; } - /// Gets or sets a value indicating whether is disabled. - public bool IsDisabled { get; set; } + /// Gets or sets a value indicating whether is disabled. + public bool IsDisabled { get; set; } - /// Gets or sets the trigger content. - public string TriggerContent { get; set; } - } + /// Gets or sets the trigger content. + public string TriggerContent { get; set; } } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlUserRoutine.cs b/src/QuickCompareModel/DatabaseSchema/SqlUserRoutine.cs index bff14fe..1481df5 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlUserRoutine.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlUserRoutine.cs @@ -2,17 +2,16 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +/// +/// Class to represent a user function or procedure in the database. +/// +public class SqlUserRoutine { - /// - /// Class to represent a user function or procedure in the database. - /// - public class SqlUserRoutine - { - /// Gets or sets the routine type. - public string RoutineType { get; set; } + /// Gets or sets the routine type. + public string RoutineType { get; set; } - /// Gets or sets the routine definition. - public string RoutineDefinition { get; set; } - } + /// Gets or sets the routine definition. + public string RoutineDefinition { get; set; } } diff --git a/src/QuickCompareModel/DatabaseSchema/SqlUserType.cs b/src/QuickCompareModel/DatabaseSchema/SqlUserType.cs index 9b08e7e..498da2f 100644 --- a/src/QuickCompareModel/DatabaseSchema/SqlUserType.cs +++ b/src/QuickCompareModel/DatabaseSchema/SqlUserType.cs @@ -2,38 +2,37 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel.DatabaseSchema +namespace QuickCompareModel.DatabaseSchema; + +/// +/// Class to represent a custom type in the database. +/// +public class SqlUserType { - /// - /// Class to represent a custom type in the database. - /// - public class SqlUserType - { - /// Gets or sets the custom type name. - public string CustomTypeName { get; set; } + /// Gets or sets the custom type name. + public string CustomTypeName { get; set; } - /// Gets or sets the schema name. - public string SchemaName { get; set; } + /// Gets or sets the schema name. + public string SchemaName { get; set; } - /// Gets or sets the underlying type name. - public string UnderlyingTypeName { get; set; } + /// Gets or sets the underlying type name. + public string UnderlyingTypeName { get; set; } - /// Gets or sets the precision. - public int? Precision { get; set; } + /// Gets or sets the precision. + public int? Precision { get; set; } - /// Gets or sets the scale. - public int? Scale { get; set; } + /// Gets or sets the scale. + public int? Scale { get; set; } - /// Gets or sets the max length. - public int? MaxLength { get; set; } + /// Gets or sets the max length. + public int? MaxLength { get; set; } - /// Gets or sets a value indicating whether is nullable. - public bool IsNullable { get; set; } + /// Gets or sets a value indicating whether is nullable. + public bool IsNullable { get; set; } - /// Gets or sets the collation name. - public string CollationName { get; set; } + /// Gets or sets the collation name. + public string CollationName { get; set; } - /// Gets or sets a value indicating whether is assembly type. - public bool IsAssemblyType { get; set; } - } + /// Gets or sets a value indicating whether is assembly type. + public bool IsAssemblyType { get; set; } } diff --git a/src/QuickCompareModel/DifferenceBuilder.cs b/src/QuickCompareModel/DifferenceBuilder.cs index 0856025..8e2ed88 100644 --- a/src/QuickCompareModel/DifferenceBuilder.cs +++ b/src/QuickCompareModel/DifferenceBuilder.cs @@ -2,956 +2,950 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel +namespace QuickCompareModel; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using QuickCompareModel.DatabaseDifferences; +using QuickCompareModel.DatabaseSchema; +using QuickCompareModel.DatabaseSchema.Enums; +using QuickCompareModel.Models; + +/// Class responsible for building a set of differences between two database instances. +/// Option settings for the database comparison. +public class DifferenceBuilder(IOptions options) : IDifferenceBuilder { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.Extensions.Options; - using QuickCompareModel.DatabaseDifferences; - using QuickCompareModel.DatabaseSchema; - using QuickCompareModel.DatabaseSchema.Enums; - - /// Class responsible for building a set of differences between two database instances. - public class DifferenceBuilder : IDifferenceBuilder + /// + /// Initialises a new instance of the class with ready instances. + /// + /// Option settings for the database comparison. + /// Instance of representing the first database to compare. + /// Instance of representing the second database to compare. + public DifferenceBuilder(IOptions options, SqlDatabase database1, SqlDatabase database2) + : this(options) { - /// - /// Initialises a new instance of the class. - /// - /// Option settings for the database comparison. - public DifferenceBuilder(IOptions options) => - this.Options = options.Value ?? throw new ArgumentNullException(nameof(options)); + this.Database1 = database1; + this.Database2 = database2; + } - /// - /// Initialises a new instance of the class with ready instances. - /// - /// Option settings for the database comparison. - /// Instance of representing the first database to compare. - /// Instance of representing the second database to compare. - public DifferenceBuilder(IOptions options, SqlDatabase database1, SqlDatabase database2) - : this(options) - { - this.Database1 = database1; - this.Database2 = database2; - } + /// + public event EventHandler ComparisonStatusChanged; - /// - public event EventHandler ComparisonStatusChanged; + /// + public QuickCompareOptions Options { get; set; } = options.Value ?? throw new ArgumentNullException(nameof(options)); - /// - public QuickCompareOptions Options { get; set; } + /// + public SqlDatabase Database1 { get; set; } - /// - public SqlDatabase Database1 { get; set; } + /// + public SqlDatabase Database2 { get; set; } - /// - public SqlDatabase Database2 { get; set; } + /// + public Differences Differences { get; set; } - /// - public Differences Differences { get; set; } + /// + public Dictionary DefinitionDifferences { get; set; } = []; - /// - public Dictionary DefinitionDifferences { get; set; } = new Dictionary(); + /// + public async Task BuildDifferencesAsync() + { + if (this.Database1 == null) + { + await this.LoadDatabaseSchemas(); + } - /// - public async Task BuildDifferencesAsync() + this.Differences = new Differences { - if (this.Database1 == null) - { - await this.LoadDatabaseSchemas(); - } + Database1 = this.Database1.FriendlyName, + Database2 = this.Database2.FriendlyName, + }; - this.Differences = new Differences - { - Database1 = this.Database1.FriendlyName, - Database2 = this.Database2.FriendlyName, - }; + this.RaiseStatusChanged("Inspecting differences"); - this.RaiseStatusChanged("Inspecting differences"); + if (this.Options.CompareProperties) + { + this.InspectDatabaseExtendedProperties(); + } - if (this.Options.CompareProperties) - { - this.InspectDatabaseExtendedProperties(); - } + if (this.Options.ComparePermissions) + { + this.InspectDatabasePermissions(); + } - if (this.Options.ComparePermissions) - { - this.InspectDatabasePermissions(); - } + this.InspectTables(); + this.InspectTableDifferences(); - this.InspectTables(); - this.InspectTableDifferences(); + if (this.Options.CompareUserTypes) + { + this.InspectUserTypes(); + } - if (this.Options.CompareUserTypes) - { - this.InspectUserTypes(); - } + if (this.Options.CompareSynonyms) + { + this.InspectSynonyms(); + } - if (this.Options.CompareSynonyms) - { - this.InspectSynonyms(); - } + if (this.Options.CompareObjects) + { + this.InspectViews(); + this.InspectRoutines(); + } - if (this.Options.CompareObjects) - { - this.InspectViews(); - this.InspectRoutines(); - } + this.RaiseStatusChanged("Difference inspection completed..."); + } - this.RaiseStatusChanged("Difference inspection completed..."); + /// Raise the status changed event. + /// Current status message. + protected virtual void RaiseStatusChanged(string message) => + this.ComparisonStatusChanged?.Invoke(this, new StatusChangedEventArgs(message)); + + /// Raise the status changed event. + /// Current status message. + /// Specified database instance. + protected virtual void RaiseStatusChanged(string message, DatabaseInstance databaseInstance) => + this.ComparisonStatusChanged?.Invoke(this, new StatusChangedEventArgs(message, databaseInstance)); + + private async Task LoadDatabaseSchemas() + { + if (string.IsNullOrEmpty(this.Options.ConnectionString1) || string.IsNullOrEmpty(this.Options.ConnectionString2)) + { + throw new InvalidOperationException("Connection strings must be set"); + } + + this.Database1 = new SqlDatabase(this.Options.ConnectionString1, this.Options); + this.Database2 = new SqlDatabase(this.Options.ConnectionString2, this.Options); + + if (this.Database1.FriendlyName.Equals(this.Database2.FriendlyName, StringComparison.CurrentCultureIgnoreCase)) + { + throw new InvalidOperationException("Connection strings must target different database instances"); } - /// Raise the status changed event. - /// Current status message. - protected virtual void RaiseStatusChanged(string message) => - this.ComparisonStatusChanged?.Invoke(this, new StatusChangedEventArgs(message)); + this.Database1.LoaderStatusChanged += (object sender, StatusChangedEventArgs e) => + this.RaiseStatusChanged(e.StatusMessage, DatabaseInstance.Database1); - /// Raise the status changed event. - /// Current status message. - /// Specified database instance. - protected virtual void RaiseStatusChanged(string message, DatabaseInstance databaseInstance) => - this.ComparisonStatusChanged?.Invoke(this, new StatusChangedEventArgs(message, databaseInstance)); + this.Database2.LoaderStatusChanged += (object sender, StatusChangedEventArgs e) => + this.RaiseStatusChanged(e.StatusMessage, DatabaseInstance.Database2); - private async Task LoadDatabaseSchemas() + await Task.WhenAll(this.Database1.PopulateSchemaModelAsync(), this.Database2.PopulateSchemaModelAsync()); + } + + private void InspectDatabaseExtendedProperties() + { + foreach (var property1 in this.Database1.ExtendedProperties.Where(x => x.Type == PropertyObjectType.Database)) { - if (string.IsNullOrEmpty(this.Options.ConnectionString1) || string.IsNullOrEmpty(this.Options.ConnectionString2)) + var diff = new ExtendedPropertyDifference(true, false); + + var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); + if (property2 != null) { - throw new InvalidOperationException("Connection strings must be set"); + diff.ExistsInDatabase2 = true; + diff.Value1 = property1.PropertyValue; + diff.Value2 = property2.PropertyValue; } - this.Database1 = new SqlDatabase(this.Options.ConnectionString1, this.Options); - this.Database2 = new SqlDatabase(this.Options.ConnectionString2, this.Options); + this.Differences.ExtendedPropertyDifferences.Add(property1.PropertyName, diff); + } - if (this.Database1.FriendlyName.ToLower() == this.Database2.FriendlyName.ToLower()) - { - throw new InvalidOperationException("Connection strings must target different database instances"); - } + foreach (var property2 in this.Database2.ExtendedProperties.Where(x => x.Type == PropertyObjectType.Database && !this.Differences.ExtendedPropertyDifferences.ContainsKey(x.PropertyName))) + { + this.Differences.ExtendedPropertyDifferences.Add(property2.PropertyName, new ExtendedPropertyDifference(false, true)); + } + } + + private void InspectDatabasePermissions() + { + foreach (var permission1 in this.Database1.Permissions.Where(x => x.Type == PermissionObjectType.Database)) + { + this.Differences.PermissionDifferences.Add( + permission1.ToString(), + new BaseDifference(true, this.Database2.Permissions.Exists(x => x.FullId == permission1.FullId))); + } - this.Database1.LoaderStatusChanged += (object sender, StatusChangedEventArgs e) => - this.RaiseStatusChanged(e.StatusMessage, DatabaseInstance.Database1); + foreach (var permission2 in this.Database2.Permissions.Where(x => x.Type == PermissionObjectType.Database && !this.Differences.PermissionDifferences.ContainsKey(x.ToString()))) + { + this.Differences.PermissionDifferences.Add(permission2.ToString(), new BaseDifference(false, true)); + } + } - this.Database2.LoaderStatusChanged += (object sender, StatusChangedEventArgs e) => - this.RaiseStatusChanged(e.StatusMessage, DatabaseInstance.Database2); + private void InspectTables() + { + foreach (var table1 in this.Database1.Tables.Keys) + { + this.Differences.TableDifferences.Add(table1, new TableDifference(true, this.Database2.Tables.Keys.Any(x => x == table1))); + } - await Task.WhenAll(this.Database1.PopulateSchemaModelAsync(), this.Database2.PopulateSchemaModelAsync()); + foreach (var table2 in this.Database2.Tables.Keys.Where(x => !this.Differences.TableDifferences.ContainsKey(x))) + { + this.Differences.TableDifferences.Add(table2, new TableDifference(false, true)); } + } - private void InspectDatabaseExtendedProperties() + private void InspectTableDifferences() + { + foreach (var fullyQualifiedTableName in this.Differences.TableDifferences.Keys.Where(x => this.Differences.TableDifferences[x].ExistsInBothDatabases)) { - foreach (var property1 in this.Database1.ExtendedProperties.Where(x => x.Type == PropertyObjectType.Database)) + if (this.Options.CompareColumns) { - var diff = new ExtendedPropertyDifference(true, false); + this.InspectTableColumns(fullyQualifiedTableName); + } - var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); - if (property2 != null) - { - diff.ExistsInDatabase2 = true; - diff.Value1 = property1.PropertyValue; - diff.Value2 = property2.PropertyValue; - } + if (this.Options.CompareIndexes) + { + this.InspectIndexes(fullyQualifiedTableName); + } - this.Differences.ExtendedPropertyDifferences.Add(property1.PropertyName, diff); + if (this.Options.CompareRelations) + { + this.InspectRelations(fullyQualifiedTableName); } - foreach (var property2 in this.Database2.ExtendedProperties.Where(x => x.Type == PropertyObjectType.Database && !this.Differences.ExtendedPropertyDifferences.ContainsKey(x.PropertyName))) + if (this.Options.ComparePermissions) { - this.Differences.ExtendedPropertyDifferences.Add(property2.PropertyName, new ExtendedPropertyDifference(false, true)); + this.InspectTablePermissions(fullyQualifiedTableName); } - } - private void InspectDatabasePermissions() - { - foreach (var permission1 in this.Database1.Permissions.Where(x => x.Type == PermissionObjectType.Database)) + if (this.Options.CompareProperties) { - this.Differences.PermissionDifferences.Add( - permission1.ToString(), - new BaseDifference(true, this.Database2.Permissions.Exists(x => x.FullId == permission1.FullId))); + this.InspectTableProperties(fullyQualifiedTableName); } - foreach (var permission2 in this.Database2.Permissions.Where(x => x.Type == PermissionObjectType.Database && !this.Differences.PermissionDifferences.ContainsKey(x.ToString()))) + if (this.Options.CompareTriggers) { - this.Differences.PermissionDifferences.Add(permission2.ToString(), new BaseDifference(false, true)); + this.InspectTriggers(fullyQualifiedTableName); } } + } - private void InspectTables() + private void InspectTableColumns(string fullyQualifiedTableName) + { + foreach (var column1 in this.Database1.Tables[fullyQualifiedTableName].ColumnDetails) { - foreach (var table1 in this.Database1.Tables.Keys) - { - this.Differences.TableDifferences.Add(table1, new TableDifference(true, this.Database2.Tables.Keys.Any(x => x == table1))); - } + var diff = new ItemWithPropertiesDifference(true, false); - foreach (var table2 in this.Database2.Tables.Keys.Where(x => !this.Differences.TableDifferences.ContainsKey(x))) + var column2 = this.Database2.Tables[fullyQualifiedTableName].ColumnDetails.FirstOrDefault(x => x.ColumnName == column1.ColumnName); + if (column2 != null) { - this.Differences.TableDifferences.Add(table2, new TableDifference(false, true)); + this.InspectColumns(fullyQualifiedTableName, diff, column1, column2); } + + this.Differences.TableDifferences[fullyQualifiedTableName].ColumnDifferences.Add(column1.ColumnName, diff); } - private void InspectTableDifferences() + foreach (var column2 in this.Database2.Tables[fullyQualifiedTableName].ColumnDetails.Where(x => !this.Differences.TableDifferences[fullyQualifiedTableName].ColumnDifferences.ContainsKey(x.ColumnName))) { - foreach (var fullyQualifiedTableName in this.Differences.TableDifferences.Keys.Where(x => this.Differences.TableDifferences[x].ExistsInBothDatabases)) - { - if (this.Options.CompareColumns) - { - this.InspectTableColumns(fullyQualifiedTableName); - } + this.Differences.TableDifferences[fullyQualifiedTableName].ColumnDifferences.Add(column2.ColumnName, new ItemWithPropertiesDifference(false, true)); + } + } - if (this.Options.CompareIndexes) - { - this.InspectIndexes(fullyQualifiedTableName); - } + private void InspectColumns(string fullyQualifiedTableName, ItemWithPropertiesDifference diff, SqlColumnDetail column1, SqlColumnDetail column2) + { + if (this.Options.CompareOrdinalPositions && column2.OrdinalPosition != column1.OrdinalPosition) + { + diff.Differences.Add($"has different ordinal position - is {column1.OrdinalPosition} in database 1 and is {column2.OrdinalPosition} in database 2"); + } - if (this.Options.CompareRelations) - { - this.InspectRelations(fullyQualifiedTableName); - } + if (column2.ColumnDefault != column1.ColumnDefault) + { + diff.Differences.Add($"has different default value - is {column1.ColumnDefault} in database 1 and is {column2.ColumnDefault} in database 2"); + } - if (this.Options.ComparePermissions) - { - this.InspectTablePermissions(fullyQualifiedTableName); - } + if (column2.IsNullable != column1.IsNullable) + { + diff.Differences.Add($"is {(column1.IsNullable ? string.Empty : "not ")}allowed null in database 1 and is {(column2.IsNullable ? string.Empty : "not ")}allowed null in database 2"); + } - if (this.Options.CompareProperties) - { - this.InspectTableProperties(fullyQualifiedTableName); - } + if (column2.DataType != column1.DataType) + { + diff.Differences.Add($"has different data type - is {column1.DataType} in database 1 and is {column2.DataType} in database 2"); + } - if (this.Options.CompareTriggers) - { - this.InspectTriggers(fullyQualifiedTableName); - } - } + if (column2.CharacterMaximumLength != column1.CharacterMaximumLength) + { + diff.Differences.Add($"has different max length - is {(column1.CharacterMaximumLength.HasValue ? column1.CharacterMaximumLength.Value.ToString("n0") : "NULL")} in database 1 and is {(column2.CharacterMaximumLength.HasValue ? column2.CharacterMaximumLength.Value.ToString("n0") : "NULL")} in database 2"); } - private void InspectTableColumns(string fullyQualifiedTableName) + if (column2.CharacterOctetLength != column1.CharacterOctetLength) { - foreach (var column1 in this.Database1.Tables[fullyQualifiedTableName].ColumnDetails) - { - var diff = new ItemWithPropertiesDifference(true, false); + diff.Differences.Add($"has different character octet length - is {(column1.CharacterOctetLength.HasValue ? column1.CharacterOctetLength.Value.ToString("n0") : "NULL")} in database 1 and is {(column2.CharacterOctetLength.HasValue ? column2.CharacterOctetLength.Value.ToString("n0") : "NULL")} in database 2"); + } - var column2 = this.Database2.Tables[fullyQualifiedTableName].ColumnDetails.FirstOrDefault(x => x.ColumnName == column1.ColumnName); - if (column2 != null) - { - this.InspectColumns(fullyQualifiedTableName, diff, column1, column2); - } + if (column2.NumericPrecision != column1.NumericPrecision) + { + diff.Differences.Add($"has different numeric precision - is {(column1.NumericPrecision.HasValue ? column1.NumericPrecision.Value.ToString() : "NULL")} in database 1 and is {(column2.NumericPrecision.HasValue ? column2.NumericPrecision.Value.ToString() : "NULL")} in database 2"); + } - this.Differences.TableDifferences[fullyQualifiedTableName].ColumnDifferences.Add(column1.ColumnName, diff); - } + if (column2.NumericPrecisionRadix != column1.NumericPrecisionRadix) + { + diff.Differences.Add($"has different numeric precision radix - is {(column1.NumericPrecisionRadix.HasValue ? column1.NumericPrecisionRadix.Value.ToString() : "NULL")} in database 1 and is {(column2.NumericPrecisionRadix.HasValue ? column2.NumericPrecisionRadix.Value.ToString() : "NULL")} in database 2"); + } - foreach (var column2 in this.Database2.Tables[fullyQualifiedTableName].ColumnDetails.Where(x => !this.Differences.TableDifferences[fullyQualifiedTableName].ColumnDifferences.ContainsKey(x.ColumnName))) - { - this.Differences.TableDifferences[fullyQualifiedTableName].ColumnDifferences.Add(column2.ColumnName, new ItemWithPropertiesDifference(false, true)); - } + if (column2.NumericScale != column1.NumericScale) + { + diff.Differences.Add($"has different numeric scale - is {(column1.NumericScale.HasValue ? column1.NumericScale.Value.ToString() : "NULL")} in database 1 and is {(column2.NumericScale.HasValue ? column2.NumericScale.Value.ToString() : "NULL")} in database 2"); } - private void InspectColumns(string fullyQualifiedTableName, ItemWithPropertiesDifference diff, SqlColumnDetail column1, SqlColumnDetail column2) + if (column2.DatetimePrecision != column1.DatetimePrecision) { - if (this.Options.CompareOrdinalPositions && column2.OrdinalPosition != column1.OrdinalPosition) - { - diff.Differences.Add($"has different ordinal position - is {column1.OrdinalPosition} in database 1 and is {column2.OrdinalPosition} in database 2"); - } + diff.Differences.Add($"has different datetime precision - is {(column1.DatetimePrecision.HasValue ? column1.DatetimePrecision.Value.ToString() : "NULL")} in database 1 and is {(column2.DatetimePrecision.HasValue ? column2.DatetimePrecision.Value.ToString() : "NULL")} in database 2"); + } - if (column2.ColumnDefault != column1.ColumnDefault) - { - diff.Differences.Add($"has different default value - is {column1.ColumnDefault} in database 1 and is {column2.ColumnDefault} in database 2"); - } + if (column2.CharacterSetName != column1.CharacterSetName) + { + diff.Differences.Add($"has different character set - is {(string.IsNullOrEmpty(column1.CharacterSetName) ? "NULL" : column1.CharacterSetName)} in database 1 and is {(string.IsNullOrEmpty(column2.CharacterSetName) ? "NULL" : column2.CharacterSetName)} in database 2"); + } - if (column2.IsNullable != column1.IsNullable) - { - diff.Differences.Add($"is {(column1.IsNullable ? string.Empty : "not ")}allowed null in database 1 and is {(column2.IsNullable ? string.Empty : "not ")}allowed null in database 2"); - } + if (this.Options.CompareCollation && column2.CollationName != column1.CollationName) + { + diff.Differences.Add($"has different collation - is {(string.IsNullOrEmpty(column1.CollationName) ? "NULL" : column1.CollationName)} in database 1 and is {(string.IsNullOrEmpty(column2.CollationName) ? "NULL" : column2.CollationName)} in database 2"); + } - if (column2.DataType != column1.DataType) - { - diff.Differences.Add($"has different data type - is {column1.DataType} in database 1 and is {column2.DataType} in database 2"); - } + if (column2.IsFullTextIndexed != column1.IsFullTextIndexed) + { + diff.Differences.Add($"is{(column1.IsFullTextIndexed ? string.Empty : " not")} full-text indexed in database 1 and is{(column2.IsFullTextIndexed ? string.Empty : " not")} full-text indexed in database 2"); + } - if (column2.CharacterMaximumLength != column1.CharacterMaximumLength) - { - diff.Differences.Add($"has different max length - is {(column1.CharacterMaximumLength.HasValue ? column1.CharacterMaximumLength.Value.ToString("n0") : "NULL")} in database 1 and is {(column2.CharacterMaximumLength.HasValue ? column2.CharacterMaximumLength.Value.ToString("n0") : "NULL")} in database 2"); - } + if (column2.IsComputed != column1.IsComputed) + { + diff.Differences.Add($"is{(column1.IsComputed ? string.Empty : " not")} computed in database 1 and is{(column2.IsComputed ? string.Empty : " not")} computed in database 2"); + } - if (column2.CharacterOctetLength != column1.CharacterOctetLength) - { - diff.Differences.Add($"has different character octet length - is {(column1.CharacterOctetLength.HasValue ? column1.CharacterOctetLength.Value.ToString("n0") : "NULL")} in database 1 and is {(column2.CharacterOctetLength.HasValue ? column2.CharacterOctetLength.Value.ToString("n0") : "NULL")} in database 2"); - } + if (column2.IsIdentity != column1.IsIdentity) + { + diff.Differences.Add($"is{(column1.IsIdentity ? string.Empty : " not")} an identity column in database 1 and is{(column2.IsIdentity ? string.Empty : " not")} an identity column in database 2"); + } - if (column2.NumericPrecision != column1.NumericPrecision) + if (column2.IsIdentity && column1.IsIdentity) + { + if (column2.IdentitySeed != column1.IdentitySeed) { - diff.Differences.Add($"has different numeric precision - is {(column1.NumericPrecision.HasValue ? column1.NumericPrecision.Value.ToString() : "NULL")} in database 1 and is {(column2.NumericPrecision.HasValue ? column2.NumericPrecision.Value.ToString() : "NULL")} in database 2"); + diff.Differences.Add($"has different identity seed - is [{column1.IdentitySeed}] in database 1 and is [{column2.IdentitySeed}] in database 2"); } - if (column2.NumericPrecisionRadix != column1.NumericPrecisionRadix) + if (column2.IdentityIncrement != column1.IdentityIncrement) { - diff.Differences.Add($"has different numeric precision radix - is {(column1.NumericPrecisionRadix.HasValue ? column1.NumericPrecisionRadix.Value.ToString() : "NULL")} in database 1 and is {(column2.NumericPrecisionRadix.HasValue ? column2.NumericPrecisionRadix.Value.ToString() : "NULL")} in database 2"); + diff.Differences.Add($"has different identity increment - is [{column1.IdentityIncrement}] in database 1 and is [{column2.IdentityIncrement}] in database 2"); } + } - if (column2.NumericScale != column1.NumericScale) - { - diff.Differences.Add($"has different numeric scale - is {(column1.NumericScale.HasValue ? column1.NumericScale.Value.ToString() : "NULL")} in database 1 and is {(column2.NumericScale.HasValue ? column2.NumericScale.Value.ToString() : "NULL")} in database 2"); - } + if (column2.IsSparse != column1.IsSparse) + { + diff.Differences.Add($"is{(column1.IsSparse ? string.Empty : " not")} sparse in database 1 and is{(column2.IsSparse ? string.Empty : " not")} sparse in database 2"); + } - if (column2.DatetimePrecision != column1.DatetimePrecision) - { - diff.Differences.Add($"has different datetime precision - is {(column1.DatetimePrecision.HasValue ? column1.DatetimePrecision.Value.ToString() : "NULL")} in database 1 and is {(column2.DatetimePrecision.HasValue ? column2.DatetimePrecision.Value.ToString() : "NULL")} in database 2"); - } + if (column2.IsColumnSet != column1.IsColumnSet) + { + diff.Differences.Add($"is{(column1.IsColumnSet ? string.Empty : " not")} a column-set in database 1 and is{(column2.IsColumnSet ? string.Empty : " not")} a column-set in database 2"); + } - if (column2.CharacterSetName != column1.CharacterSetName) - { - diff.Differences.Add($"has different character set - is {(string.IsNullOrEmpty(column1.CharacterSetName) ? "NULL" : column1.CharacterSetName)} in database 1 and is {(string.IsNullOrEmpty(column2.CharacterSetName) ? "NULL" : column2.CharacterSetName)} in database 2"); - } + if (column2.IsRowGuid != column1.IsRowGuid) + { + diff.Differences.Add($"is{(column1.IsRowGuid ? string.Empty : " not")} a row-guid in database 1 and is{(column2.IsRowGuid ? string.Empty : " not")} a row-guid in database 2"); + } - if (this.Options.CompareCollation && column2.CollationName != column1.CollationName) - { - diff.Differences.Add($"has different collation - is {(string.IsNullOrEmpty(column1.CollationName) ? "NULL" : column1.CollationName)} in database 1 and is {(string.IsNullOrEmpty(column2.CollationName) ? "NULL" : column2.CollationName)} in database 2"); - } + if (column2.DomainName.PrependSchemaName(column2.DomainSchema) != column1.DomainName.PrependSchemaName(column1.DomainSchema)) + { + diff.Differences.Add($"has different custom datatype - is {(string.IsNullOrEmpty(column1.DomainName) ? "NULL" : column1.DomainName.PrependSchemaName(column1.DomainSchema))} in database 1 and is {(string.IsNullOrEmpty(column2.DomainName) ? "NULL" : column2.DomainName.PrependSchemaName(column2.DomainSchema))} in database 2"); + } - if (column2.IsFullTextIndexed != column1.IsFullTextIndexed) - { - diff.Differences.Add($"is{(column1.IsFullTextIndexed ? string.Empty : " not")} full-text indexed in database 1 and is{(column2.IsFullTextIndexed ? string.Empty : " not")} full-text indexed in database 2"); - } + if (this.Database2.Tables[fullyQualifiedTableName].ColumnHasUniqueIndex(column2.ColumnName) != this.Database1.Tables[fullyQualifiedTableName].ColumnHasUniqueIndex(column1.ColumnName)) + { + diff.Differences.Add($"{(this.Database1.Tables[fullyQualifiedTableName].ColumnHasUniqueIndex(column1.ColumnName) ? "has" : "does not have")} a unique constraint in database 1 and {(this.Database2.Tables[fullyQualifiedTableName].ColumnHasUniqueIndex(column2.ColumnName) ? "has" : "does not have")} a unique constraint in database 2"); + } - if (column2.IsComputed != column1.IsComputed) - { - diff.Differences.Add($"is{(column1.IsComputed ? string.Empty : " not")} computed in database 1 and is{(column2.IsComputed ? string.Empty : " not")} computed in database 2"); - } + if (this.Options.CompareProperties) + { + this.InspectColumnProperties(fullyQualifiedTableName, column2.ColumnName, diff); + } + + diff.ExistsInDatabase2 = true; + } + + private void InspectColumnProperties(string fullyQualifiedTableName, string columnName, ItemWithPropertiesDifference columnDiff) + { + var hasFoundColumn1Description = false; + + foreach (var property1 in this.Database1.ExtendedProperties.Where(x => x.Type == PropertyObjectType.TableColumn && x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName && x.ColumnName == columnName)) + { + var diff = new ExtendedPropertyDifference(true, false); - if (column2.IsIdentity != column1.IsIdentity) + var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); + if (property2 != null) { - diff.Differences.Add($"is{(column1.IsIdentity ? string.Empty : " not")} an identity column in database 1 and is{(column2.IsIdentity ? string.Empty : " not")} an identity column in database 2"); + diff.ExistsInDatabase2 = true; + diff.Value1 = property1.PropertyValue; + diff.Value2 = property2.PropertyValue; } - if (column2.IsIdentity && column1.IsIdentity) + if (property1.PropertyName == "MS_Description") { - if (column2.IdentitySeed != column1.IdentitySeed) + hasFoundColumn1Description = true; + if (!diff.ExistsInDatabase2) { - diff.Differences.Add($"has different identity seed - is [{column1.IdentitySeed}] in database 1 and is [{column2.IdentitySeed}] in database 2"); + columnDiff.Differences.Add("description exists in database 1 and does not exist in database 2"); } - - if (column2.IdentityIncrement != column1.IdentityIncrement) + else if (diff.Value1 != diff.Value2) { - diff.Differences.Add($"has different identity increment - is [{column1.IdentityIncrement}] in database 1 and is [{column2.IdentityIncrement}] in database 2"); + columnDiff.Differences.Add($"has different description - is [{diff.Value1}] in database 1 and is [{diff.Value2}] in database 2"); } } - - if (column2.IsSparse != column1.IsSparse) - { - diff.Differences.Add($"is{(column1.IsSparse ? string.Empty : " not")} sparse in database 1 and is{(column2.IsSparse ? string.Empty : " not")} sparse in database 2"); - } - - if (column2.IsColumnSet != column1.IsColumnSet) - { - diff.Differences.Add($"is{(column1.IsColumnSet ? string.Empty : " not")} a column-set in database 1 and is{(column2.IsColumnSet ? string.Empty : " not")} a column-set in database 2"); - } - - if (column2.IsRowGuid != column1.IsRowGuid) - { - diff.Differences.Add($"is{(column1.IsRowGuid ? string.Empty : " not")} a row-guid in database 1 and is{(column2.IsRowGuid ? string.Empty : " not")} a row-guid in database 2"); - } - - if (column2.DomainName.PrependSchemaName(column2.DomainSchema) != column1.DomainName.PrependSchemaName(column1.DomainSchema)) + else { - diff.Differences.Add($"has different custom datatype - is {(string.IsNullOrEmpty(column1.DomainName) ? "NULL" : column1.DomainName.PrependSchemaName(column1.DomainSchema))} in database 1 and is {(string.IsNullOrEmpty(column2.DomainName) ? "NULL" : column2.DomainName.PrependSchemaName(column2.DomainSchema))} in database 2"); + columnDiff.ExtendedPropertyDifferences.Add(property1.PropertyName, diff); } + } - if (this.Database2.Tables[fullyQualifiedTableName].ColumnHasUniqueIndex(column2.ColumnName) != this.Database1.Tables[fullyQualifiedTableName].ColumnHasUniqueIndex(column1.ColumnName)) + foreach (var property2 in this.Database2.ExtendedProperties.Where(x => x.Type == PropertyObjectType.TableColumn && x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName && x.ColumnName == columnName)) + { + if (property2.PropertyName == "MS_Description") { - diff.Differences.Add($"{(this.Database1.Tables[fullyQualifiedTableName].ColumnHasUniqueIndex(column1.ColumnName) ? "has" : "does not have")} a unique constraint in database 1 and {(this.Database2.Tables[fullyQualifiedTableName].ColumnHasUniqueIndex(column2.ColumnName) ? "has" : "does not have")} a unique constraint in database 2"); + if (!hasFoundColumn1Description) + { + columnDiff.Differences.Add("description exists in database 2 and does not exist in database 1"); + } } - - if (this.Options.CompareProperties) + else if (!columnDiff.ExtendedPropertyDifferences.ContainsKey(property2.PropertyName)) { - this.InspectColumnProperties(fullyQualifiedTableName, column2.ColumnName, diff); + columnDiff.ExtendedPropertyDifferences.Add(property2.PropertyName, new ExtendedPropertyDifference(false, true)); } - - diff.ExistsInDatabase2 = true; } + } - private void InspectColumnProperties(string fullyQualifiedTableName, string columnName, ItemWithPropertiesDifference columnDiff) + private void InspectIndexes(string fullyQualifiedTableName) + { + foreach (var index1 in this.Database1.Tables[fullyQualifiedTableName].Indexes) { - var hasFoundColumn1Description = false; + var diff = new ItemWithPropertiesDifference(true, false, index1.ItemType); - foreach (var property1 in this.Database1.ExtendedProperties.Where(x => x.Type == PropertyObjectType.TableColumn && x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName && x.ColumnName == columnName)) + var index2 = this.Database2.Tables[fullyQualifiedTableName].Indexes.FirstOrDefault(x => x.FullId == index1.FullId); + if (index2 != null) { - var diff = new ExtendedPropertyDifference(true, false); - - var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); - if (property2 != null) + if (index2.Clustered != index1.Clustered) { - diff.ExistsInDatabase2 = true; - diff.Value1 = property1.PropertyValue; - diff.Value2 = property2.PropertyValue; + diff.Differences.Add($"has different clustering - is{(index1.Clustered ? string.Empty : " not")} clustered in database 1 and is{(index2.Clustered ? string.Empty : " not")} clustered in database 2"); } - if (property1.PropertyName == "MS_Description") + if (index2.Unique != index1.Unique) { - hasFoundColumn1Description = true; - if (!diff.ExistsInDatabase2) - { - columnDiff.Differences.Add("description exists in database 1 and does not exist in database 2"); - } - else if (diff.Value1 != diff.Value2) - { - columnDiff.Differences.Add($"has different description - is [{diff.Value1}] in database 1 and is [{diff.Value2}] in database 2"); - } + diff.Differences.Add($"has different uniqueness - is{(index1.Unique ? string.Empty : " not")} unique in database 1 and is{(index2.Unique ? string.Empty : " not")} unique in database 2"); } - else + + if (index2.IsUniqueKey != index1.IsUniqueKey) { - columnDiff.ExtendedPropertyDifferences.Add(property1.PropertyName, diff); + diff.Differences.Add($"has different type - {(index1.IsUniqueKey ? "unique key" : "index")} in database 1 and {(index2.Unique ? string.Empty : " not")} in database 2"); } - } - foreach (var property2 in this.Database2.ExtendedProperties.Where(x => x.Type == PropertyObjectType.TableColumn && x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName && x.ColumnName == columnName)) - { - if (property2.PropertyName == "MS_Description") + if (index2.IsPrimaryKey != index1.IsPrimaryKey) { - if (!hasFoundColumn1Description) - { - columnDiff.Differences.Add("description exists in database 2 and does not exist in database 1"); - } + diff.Differences.Add($"has different primary - is{(index1.IsPrimaryKey ? string.Empty : " not")} a primary key in database 1 and is{(index2.IsPrimaryKey ? string.Empty : " not")} a primary key in database 2"); } - else if (!columnDiff.ExtendedPropertyDifferences.ContainsKey(property2.PropertyName)) + + if (index2.FileGroup != index1.FileGroup) { - columnDiff.ExtendedPropertyDifferences.Add(property2.PropertyName, new ExtendedPropertyDifference(false, true)); + diff.Differences.Add($"has different filegroup - [{index1.FileGroup}] in database 1 and [{index2.FileGroup}] in database 2"); } - } - } - private void InspectIndexes(string fullyQualifiedTableName) - { - foreach (var index1 in this.Database1.Tables[fullyQualifiedTableName].Indexes) - { - var diff = new ItemWithPropertiesDifference(true, false, index1.ItemType); - - var index2 = this.Database2.Tables[fullyQualifiedTableName].Indexes.FirstOrDefault(x => x.FullId == index1.FullId); - if (index2 != null) + if (index2.ColumnsToString != index1.ColumnsToString) { - if (index2.Clustered != index1.Clustered) - { - diff.Differences.Add($"has different clustering - is{(index1.Clustered ? string.Empty : " not")} clustered in database 1 and is{(index2.Clustered ? string.Empty : " not")} clustered in database 2"); - } - - if (index2.Unique != index1.Unique) - { - diff.Differences.Add($"has different uniqueness - is{(index1.Unique ? string.Empty : " not")} unique in database 1 and is{(index2.Unique ? string.Empty : " not")} unique in database 2"); - } - - if (index2.IsUniqueKey != index1.IsUniqueKey) - { - diff.Differences.Add($"has different type - {(index1.IsUniqueKey ? "unique key" : "index")} in database 1 and {(index2.Unique ? string.Empty : " not")} in database 2"); - } - - if (index2.IsPrimaryKey != index1.IsPrimaryKey) + foreach (var column in index1.Columns.Keys) { - diff.Differences.Add($"has different primary - is{(index1.IsPrimaryKey ? string.Empty : " not")} a primary key in database 1 and is{(index2.IsPrimaryKey ? string.Empty : " not")} a primary key in database 2"); - } - - if (index2.FileGroup != index1.FileGroup) - { - diff.Differences.Add($"has different filegroup - [{index1.FileGroup}] in database 1 and [{index2.FileGroup}] in database 2"); - } - - if (index2.ColumnsToString != index1.ColumnsToString) - { - foreach (var column in index1.Columns.Keys) + if (index2.Columns.TryGetValue(column, out bool value)) { - if (index2.Columns.TryGetValue(column, out bool value)) - { - if (index1.Columns[column] != value) - { - diff.Differences.Add($"[{column}] has different ordering - {(index1.Columns[column] ? "a" : "de")}scending on database 1 and {(value ? "a" : "de")}scending on database 2"); - } - } - else + if (index1.Columns[column] != value) { - diff.Differences.Add($"[{column}] column does not exist in database 2 index"); + diff.Differences.Add($"[{column}] has different ordering - {(index1.Columns[column] ? "a" : "de")}scending on database 1 and {(value ? "a" : "de")}scending on database 2"); } } - - foreach (var column in index2.Columns.Keys.Where(x => !index1.Columns.ContainsKey(x))) + else { - diff.Differences.Add($"[{column}] column does not exist in database 1 index"); + diff.Differences.Add($"[{column}] column does not exist in database 2 index"); } } - if (index2.IncludedColumnsToString != index1.IncludedColumnsToString) + foreach (var column in index2.Columns.Keys.Where(x => !index1.Columns.ContainsKey(x))) + { + diff.Differences.Add($"[{column}] column does not exist in database 1 index"); + } + } + + if (index2.IncludedColumnsToString != index1.IncludedColumnsToString) + { + foreach (var column in index1.IncludedColumns.Keys) { - foreach (var column in index1.IncludedColumns.Keys) + if (index2.IncludedColumns.TryGetValue(column, out bool value)) { - if (index2.IncludedColumns.TryGetValue(column, out bool value)) - { - if (index1.IncludedColumns[column] != value) - { - diff.Differences.Add($"[{column}] \"included column\" has different ordering - {(index1.IncludedColumns[column] ? "a" : "de")}scending on database 1 and {(value ? "a" : "de")}scending on database 2"); - } - } - else + if (index1.IncludedColumns[column] != value) { - diff.Differences.Add($"[{column}] \"included column\" does not exist in database 2 index"); + diff.Differences.Add($"[{column}] \"included column\" has different ordering - {(index1.IncludedColumns[column] ? "a" : "de")}scending on database 1 and {(value ? "a" : "de")}scending on database 2"); } } - - foreach (var column in index2.IncludedColumns.Keys.Where(x => !index1.IncludedColumns.ContainsKey(x))) + else { - diff.Differences.Add($"[{column}] \"included column\" does not exist in database 1 index"); + diff.Differences.Add($"[{column}] \"included column\" does not exist in database 2 index"); } } - if (this.Options.CompareProperties) + foreach (var column in index2.IncludedColumns.Keys.Where(x => !index1.IncludedColumns.ContainsKey(x))) { - this.InspectIndexProperties(fullyQualifiedTableName, index2.IndexName, diff); + diff.Differences.Add($"[{column}] \"included column\" does not exist in database 1 index"); } + } - diff.ExistsInDatabase2 = true; + if (this.Options.CompareProperties) + { + this.InspectIndexProperties(fullyQualifiedTableName, index2.IndexName, diff); } - this.Differences.TableDifferences[fullyQualifiedTableName].IndexDifferences.Add(index1.IndexName, diff); + diff.ExistsInDatabase2 = true; } - foreach (var index in this.Database2.Tables[fullyQualifiedTableName].Indexes.Where(x => - !this.Differences.TableDifferences[fullyQualifiedTableName].IndexDifferences.ContainsKey(x.IndexName))) - { - this.Differences.TableDifferences[fullyQualifiedTableName].IndexDifferences.Add(index.IndexName, new ItemWithPropertiesDifference(false, true, index.ItemType)); - } + this.Differences.TableDifferences[fullyQualifiedTableName].IndexDifferences.Add(index1.IndexName, diff); } - private void InspectIndexProperties(string fullyQualifiedTableName, string indexName, ItemWithPropertiesDifference indexDiff) + foreach (var index in this.Database2.Tables[fullyQualifiedTableName].Indexes.Where(x => + !this.Differences.TableDifferences[fullyQualifiedTableName].IndexDifferences.ContainsKey(x.IndexName))) { - foreach (var property1 in this.Database1.ExtendedProperties) - { - var propertyTableName = property1.TableName.PrependSchemaName(property1.TableSchema); - if (property1.Type == PropertyObjectType.Index && propertyTableName == fullyQualifiedTableName && property1.IndexName == indexName) - { - var diff = new ExtendedPropertyDifference(true, false); - var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); - if (property2 != null) - { - diff.ExistsInDatabase2 = true; - diff.Value1 = property1.PropertyValue; - diff.Value2 = property2.PropertyValue; - } + this.Differences.TableDifferences[fullyQualifiedTableName].IndexDifferences.Add(index.IndexName, new ItemWithPropertiesDifference(false, true, index.ItemType)); + } + } - indexDiff.ExtendedPropertyDifferences.Add(property1.PropertyName, diff); + private void InspectIndexProperties(string fullyQualifiedTableName, string indexName, ItemWithPropertiesDifference indexDiff) + { + foreach (var property1 in this.Database1.ExtendedProperties) + { + var propertyTableName = property1.TableName.PrependSchemaName(property1.TableSchema); + if (property1.Type == PropertyObjectType.Index && propertyTableName == fullyQualifiedTableName && property1.IndexName == indexName) + { + var diff = new ExtendedPropertyDifference(true, false); + var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); + if (property2 != null) + { + diff.ExistsInDatabase2 = true; + diff.Value1 = property1.PropertyValue; + diff.Value2 = property2.PropertyValue; } - } - foreach (var property in this.Database2.ExtendedProperties.Where(x => - x.PropertyType == "INDEX" && - x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName && - x.IndexName == indexName && - !indexDiff.ExtendedPropertyDifferences.ContainsKey(x.PropertyName))) - { - indexDiff.ExtendedPropertyDifferences.Add(property.PropertyName, new ExtendedPropertyDifference(false, true)); + indexDiff.ExtendedPropertyDifferences.Add(property1.PropertyName, diff); } } - private void InspectRelations(string fullyQualifiedTableName) + foreach (var property in this.Database2.ExtendedProperties.Where(x => + x.PropertyType == "INDEX" && + x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName && + x.IndexName == indexName && + !indexDiff.ExtendedPropertyDifferences.ContainsKey(x.PropertyName))) { - foreach (var relation1 in this.Database1.Tables[fullyQualifiedTableName].Relations) - { - var diff = new ItemDifference(true, false); + indexDiff.ExtendedPropertyDifferences.Add(property.PropertyName, new ExtendedPropertyDifference(false, true)); + } + } - var relation2 = this.Database2.Tables[fullyQualifiedTableName].Relations.FirstOrDefault(x => x.RelationName == relation1.RelationName); - if (relation2 != null) - { - if (relation2.ChildColumns != relation1.ChildColumns) - { - diff.Differences.Add($"has different child column list - is \"{relation1.ChildColumns}\" in database 1 and is \"{relation2.ChildColumns}\" in database 2"); - } + private void InspectRelations(string fullyQualifiedTableName) + { + foreach (var relation1 in this.Database1.Tables[fullyQualifiedTableName].Relations) + { + var diff = new ItemDifference(true, false); - if (relation2.ParentColumns != relation1.ParentColumns) - { - diff.Differences.Add($"has different parent column list - is \"{relation1.ParentColumns}\" in database 1 and is \"{relation2.ParentColumns}\" in database 2"); - } + var relation2 = this.Database2.Tables[fullyQualifiedTableName].Relations.FirstOrDefault(x => x.RelationName == relation1.RelationName); + if (relation2 != null) + { + if (relation2.ChildColumns != relation1.ChildColumns) + { + diff.Differences.Add($"has different child column list - is \"{relation1.ChildColumns}\" in database 1 and is \"{relation2.ChildColumns}\" in database 2"); + } - if (relation2.DeleteRule != relation1.DeleteRule) - { - diff.Differences.Add($"has different delete rule - is \"{relation1.DeleteRule}\" in database 1 and is \"{relation2.DeleteRule}\" in database 2"); - } + if (relation2.ParentColumns != relation1.ParentColumns) + { + diff.Differences.Add($"has different parent column list - is \"{relation1.ParentColumns}\" in database 1 and is \"{relation2.ParentColumns}\" in database 2"); + } - if (relation2.UpdateRule != relation1.UpdateRule) - { - diff.Differences.Add($"has different update rule - is \"{relation1.UpdateRule}\" in database 1 and is \"{relation2.UpdateRule}\" in database 2"); - } + if (relation2.DeleteRule != relation1.DeleteRule) + { + diff.Differences.Add($"has different delete rule - is \"{relation1.DeleteRule}\" in database 1 and is \"{relation2.DeleteRule}\" in database 2"); + } - diff.ExistsInDatabase2 = true; + if (relation2.UpdateRule != relation1.UpdateRule) + { + diff.Differences.Add($"has different update rule - is \"{relation1.UpdateRule}\" in database 1 and is \"{relation2.UpdateRule}\" in database 2"); } - this.Differences.TableDifferences[fullyQualifiedTableName].RelationshipDifferences.Add(relation1.RelationName, diff); + diff.ExistsInDatabase2 = true; } - foreach (var relation in this.Database2.Tables[fullyQualifiedTableName].Relations.Where(x => !this.Differences.TableDifferences[fullyQualifiedTableName].RelationshipDifferences.ContainsKey(x.RelationName))) - { - this.Differences.TableDifferences[fullyQualifiedTableName].RelationshipDifferences.Add(relation.RelationName, new ItemDifference(false, true)); - } + this.Differences.TableDifferences[fullyQualifiedTableName].RelationshipDifferences.Add(relation1.RelationName, diff); } - private void InspectTablePermissions(string fullyQualifiedTableName) + foreach (var relation in this.Database2.Tables[fullyQualifiedTableName].Relations.Where(x => !this.Differences.TableDifferences[fullyQualifiedTableName].RelationshipDifferences.ContainsKey(x.RelationName))) { - foreach (var permission1 in this.Database1.Permissions) - { - var permissionTableName = permission1.ObjectName.PrependSchemaName(permission1.ObjectSchema); - if (permission1.Type == PermissionObjectType.UserTable && permissionTableName == fullyQualifiedTableName) - { - this.Differences.TableDifferences[fullyQualifiedTableName].PermissionDifferences.Add( - permission1.ToString(), - new BaseDifference(true, this.Database2.Permissions.Exists(x => x.FullId == permission1.FullId))); - } - } + this.Differences.TableDifferences[fullyQualifiedTableName].RelationshipDifferences.Add(relation.RelationName, new ItemDifference(false, true)); + } + } - foreach (var permission in this.Database2.Permissions.Where(x => - x.ObjectType == "USER_TABLE" && - x.ObjectName.PrependSchemaName(x.ObjectSchema) == fullyQualifiedTableName && - !this.Differences.TableDifferences[fullyQualifiedTableName].PermissionDifferences.ContainsKey(x.ToString()))) + private void InspectTablePermissions(string fullyQualifiedTableName) + { + foreach (var permission1 in this.Database1.Permissions) + { + var permissionTableName = permission1.ObjectName.PrependSchemaName(permission1.ObjectSchema); + if (permission1.Type == PermissionObjectType.UserTable && permissionTableName == fullyQualifiedTableName) { - this.Differences.TableDifferences[fullyQualifiedTableName].PermissionDifferences.Add(permission.ToString(), new ExtendedPropertyDifference(false, true)); + this.Differences.TableDifferences[fullyQualifiedTableName].PermissionDifferences.Add( + permission1.ToString(), + new BaseDifference(true, this.Database2.Permissions.Exists(x => x.FullId == permission1.FullId))); } } - private void InspectTableProperties(string fullyQualifiedTableName) + foreach (var permission in this.Database2.Permissions.Where(x => + x.ObjectType == "USER_TABLE" && + x.ObjectName.PrependSchemaName(x.ObjectSchema) == fullyQualifiedTableName && + !this.Differences.TableDifferences[fullyQualifiedTableName].PermissionDifferences.ContainsKey(x.ToString()))) { - foreach (var property1 in this.Database1.ExtendedProperties) - { - var propertyTableName = property1.TableName.PrependSchemaName(property1.TableSchema); - if (property1.Type == PropertyObjectType.Table && propertyTableName == fullyQualifiedTableName) - { - var diff = new ExtendedPropertyDifference(true, false); + this.Differences.TableDifferences[fullyQualifiedTableName].PermissionDifferences.Add(permission.ToString(), new ExtendedPropertyDifference(false, true)); + } + } - var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); - if (property2 != null) - { - diff.ExistsInDatabase2 = true; - diff.Value1 = property1.PropertyValue; - diff.Value2 = property2.PropertyValue; - } + private void InspectTableProperties(string fullyQualifiedTableName) + { + foreach (var property1 in this.Database1.ExtendedProperties) + { + var propertyTableName = property1.TableName.PrependSchemaName(property1.TableSchema); + if (property1.Type == PropertyObjectType.Table && propertyTableName == fullyQualifiedTableName) + { + var diff = new ExtendedPropertyDifference(true, false); - this.Differences.TableDifferences[fullyQualifiedTableName].ExtendedPropertyDifferences.Add(property1.PropertyName, diff); + var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); + if (property2 != null) + { + diff.ExistsInDatabase2 = true; + diff.Value1 = property1.PropertyValue; + diff.Value2 = property2.PropertyValue; } - } - foreach (var property in this.Database2.ExtendedProperties.Where(x => - x.ColumnName == null && - x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName && - !this.Differences.TableDifferences[fullyQualifiedTableName].ExtendedPropertyDifferences.ContainsKey(x.PropertyName))) - { - this.Differences.TableDifferences[fullyQualifiedTableName].ExtendedPropertyDifferences.Add(property.PropertyName, new ExtendedPropertyDifference(false, true)); + this.Differences.TableDifferences[fullyQualifiedTableName].ExtendedPropertyDifferences.Add(property1.PropertyName, diff); } } - private void InspectTriggers(string fullyQualifiedTableName) + foreach (var property in this.Database2.ExtendedProperties.Where(x => + x.ColumnName == null && + x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName && + !this.Differences.TableDifferences[fullyQualifiedTableName].ExtendedPropertyDifferences.ContainsKey(x.PropertyName))) { - foreach (var trigger1 in this.Database1.Tables[fullyQualifiedTableName].Triggers) - { - var diff = new ItemDifference(true, false); + this.Differences.TableDifferences[fullyQualifiedTableName].ExtendedPropertyDifferences.Add(property.PropertyName, new ExtendedPropertyDifference(false, true)); + } + } - var trigger2 = this.Database2.Tables[fullyQualifiedTableName].Triggers.FirstOrDefault(x => x.TriggerName == trigger1.TriggerName && x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName); - if (trigger2 != null) - { - if (trigger2.FileGroup != trigger1.FileGroup) - { - diff.Differences.Add($"has different filegroup - is {trigger1.FileGroup} in database 1 and is {trigger2.FileGroup} in database 2"); - } + private void InspectTriggers(string fullyQualifiedTableName) + { + foreach (var trigger1 in this.Database1.Tables[fullyQualifiedTableName].Triggers) + { + var diff = new ItemDifference(true, false); - if (trigger2.TriggerOwner != trigger1.TriggerOwner) - { - diff.Differences.Add($"has different owner - is {trigger1.TriggerOwner} in database 1 and is {trigger2.TriggerOwner} in database 2"); - } + var trigger2 = this.Database2.Tables[fullyQualifiedTableName].Triggers.FirstOrDefault(x => x.TriggerName == trigger1.TriggerName && x.TableName.PrependSchemaName(x.TableSchema) == fullyQualifiedTableName); + if (trigger2 != null) + { + if (trigger2.FileGroup != trigger1.FileGroup) + { + diff.Differences.Add($"has different filegroup - is {trigger1.FileGroup} in database 1 and is {trigger2.FileGroup} in database 2"); + } - if (trigger2.IsUpdate != trigger1.IsUpdate) - { - diff.Differences.Add($"has different update - is {(trigger1.IsUpdate ? string.Empty : "not ")}update in database 1 and is {(trigger2.IsUpdate ? string.Empty : "not ")}update in database 2"); - } + if (trigger2.TriggerOwner != trigger1.TriggerOwner) + { + diff.Differences.Add($"has different owner - is {trigger1.TriggerOwner} in database 1 and is {trigger2.TriggerOwner} in database 2"); + } - if (trigger2.IsDelete != trigger1.IsDelete) - { - diff.Differences.Add($"has different delete - is {(trigger1.IsDelete ? string.Empty : "not ")}delete in database 1 and is {(trigger2.IsDelete ? string.Empty : "not ")}delete in database 2"); - } + if (trigger2.IsUpdate != trigger1.IsUpdate) + { + diff.Differences.Add($"has different update - is {(trigger1.IsUpdate ? string.Empty : "not ")}update in database 1 and is {(trigger2.IsUpdate ? string.Empty : "not ")}update in database 2"); + } - if (trigger2.IsInsert != trigger1.IsInsert) - { - diff.Differences.Add($"has different insert - is {(trigger1.IsInsert ? string.Empty : "not ")}insert in database 1 and is {(trigger2.IsInsert ? string.Empty : "not ")}insert in database 2"); - } + if (trigger2.IsDelete != trigger1.IsDelete) + { + diff.Differences.Add($"has different delete - is {(trigger1.IsDelete ? string.Empty : "not ")}delete in database 1 and is {(trigger2.IsDelete ? string.Empty : "not ")}delete in database 2"); + } - if (trigger2.IsAfter != trigger1.IsAfter) - { - diff.Differences.Add($"has different after - is {(trigger1.IsAfter ? string.Empty : "not ")}after in database 1 and is {(trigger2.IsAfter ? string.Empty : "not ")}after in database 2"); - } + if (trigger2.IsInsert != trigger1.IsInsert) + { + diff.Differences.Add($"has different insert - is {(trigger1.IsInsert ? string.Empty : "not ")}insert in database 1 and is {(trigger2.IsInsert ? string.Empty : "not ")}insert in database 2"); + } - if (trigger2.IsInsteadOf != trigger1.IsInsteadOf) - { - diff.Differences.Add($"has different instead-of - is {(trigger1.IsInsteadOf ? string.Empty : "not ")}instead-of in database 1 and is {(trigger2.IsInsteadOf ? string.Empty : "not ")}instead-of in database 2"); - } + if (trigger2.IsAfter != trigger1.IsAfter) + { + diff.Differences.Add($"has different after - is {(trigger1.IsAfter ? string.Empty : "not ")}after in database 1 and is {(trigger2.IsAfter ? string.Empty : "not ")}after in database 2"); + } - if (trigger2.IsDisabled != trigger1.IsDisabled) - { - diff.Differences.Add($"has different disabled - is {(trigger1.IsDisabled ? string.Empty : "not ")}disabled in database 1 and is {(trigger2.IsDisabled ? string.Empty : "not ")}disabled in database 2"); - } + if (trigger2.IsInsteadOf != trigger1.IsInsteadOf) + { + diff.Differences.Add($"has different instead-of - is {(trigger1.IsInsteadOf ? string.Empty : "not ")}instead-of in database 1 and is {(trigger2.IsInsteadOf ? string.Empty : "not ")}instead-of in database 2"); + } - if (BaseDifference.CleanDefinitionText(trigger1.TriggerContent, true) != BaseDifference.CleanDefinitionText(trigger2.TriggerContent, true)) - { - diff.Differences.Add("definitions are different"); - } + if (trigger2.IsDisabled != trigger1.IsDisabled) + { + diff.Differences.Add($"has different disabled - is {(trigger1.IsDisabled ? string.Empty : "not ")}disabled in database 1 and is {(trigger2.IsDisabled ? string.Empty : "not ")}disabled in database 2"); + } - diff.ExistsInDatabase2 = true; + if (BaseDifference.CleanDefinitionText(trigger1.TriggerContent, true) != BaseDifference.CleanDefinitionText(trigger2.TriggerContent, true)) + { + diff.Differences.Add("definitions are different"); } - this.Differences.TableDifferences[fullyQualifiedTableName].TriggerDifferences.Add(trigger1.TriggerName, diff); + diff.ExistsInDatabase2 = true; } - foreach (var trigger2 in this.Database2.Tables[fullyQualifiedTableName].Triggers.Where(x => !this.Differences.TableDifferences[fullyQualifiedTableName].TriggerDifferences.ContainsKey(x.TriggerName))) - { - this.Differences.TableDifferences[fullyQualifiedTableName].TriggerDifferences.Add(trigger2.TriggerName, new ItemDifference(false, true)); - } + this.Differences.TableDifferences[fullyQualifiedTableName].TriggerDifferences.Add(trigger1.TriggerName, diff); } - private void InspectUserTypes() + foreach (var trigger2 in this.Database2.Tables[fullyQualifiedTableName].Triggers.Where(x => !this.Differences.TableDifferences[fullyQualifiedTableName].TriggerDifferences.ContainsKey(x.TriggerName))) { - foreach (var userTypeName in this.Database1.UserTypes.Keys) - { - var diff = new ItemDifference(true, false); + this.Differences.TableDifferences[fullyQualifiedTableName].TriggerDifferences.Add(trigger2.TriggerName, new ItemDifference(false, true)); + } + } - var userType = this.Database2.UserTypes.FirstOrDefault(x => x.Key == userTypeName); - if (!userType.Equals(default(KeyValuePair))) - { - var item1 = this.Database1.UserTypes[userTypeName]; - var item2 = this.Database2.UserTypes[userTypeName]; + private void InspectUserTypes() + { + foreach (var userTypeName in this.Database1.UserTypes.Keys) + { + var diff = new ItemDifference(true, false); - if (item1.UnderlyingTypeName != item2.UnderlyingTypeName) - { - diff.Differences.Add($"has different underlying type - is {item1.UnderlyingTypeName} in database 1 and is {item2.UnderlyingTypeName} in database 2"); - } + var userType = this.Database2.UserTypes.FirstOrDefault(x => x.Key == userTypeName); + if (!userType.Equals(default(KeyValuePair))) + { + var item1 = this.Database1.UserTypes[userTypeName]; + var item2 = this.Database2.UserTypes[userTypeName]; - if (item1.Precision != item2.Precision) - { - diff.Differences.Add($"has different precision - is {(item1.Precision.HasValue ? item1.Precision.Value.ToString() : "NULL")} in database 1 and is {(item2.Precision.HasValue ? item2.Precision.Value.ToString() : "NULL")} in database 2"); - } + if (item1.UnderlyingTypeName != item2.UnderlyingTypeName) + { + diff.Differences.Add($"has different underlying type - is {item1.UnderlyingTypeName} in database 1 and is {item2.UnderlyingTypeName} in database 2"); + } - if (item1.Scale != item2.Scale) - { - diff.Differences.Add($"has different scale - is {(item1.Scale.HasValue ? item1.Scale.Value.ToString() : "NULL")} in database 1 and is {(item2.Scale.HasValue ? item2.Scale.Value.ToString() : "NULL")} in database 2"); - } + if (item1.Precision != item2.Precision) + { + diff.Differences.Add($"has different precision - is {(item1.Precision.HasValue ? item1.Precision.Value.ToString() : "NULL")} in database 1 and is {(item2.Precision.HasValue ? item2.Precision.Value.ToString() : "NULL")} in database 2"); + } - if (item1.MaxLength != item2.MaxLength) - { - diff.Differences.Add($"has different max length - is {(item1.MaxLength.HasValue ? item1.MaxLength.Value.ToString() : "NULL")} in database 1 and is {(item2.MaxLength.HasValue ? item2.MaxLength.Value.ToString() : "NULL")} in database 2"); - } + if (item1.Scale != item2.Scale) + { + diff.Differences.Add($"has different scale - is {(item1.Scale.HasValue ? item1.Scale.Value.ToString() : "NULL")} in database 1 and is {(item2.Scale.HasValue ? item2.Scale.Value.ToString() : "NULL")} in database 2"); + } - if (item1.IsNullable != item2.IsNullable) - { - diff.Differences.Add($"is{(item1.IsNullable ? string.Empty : " not")} nullable in database 1 and is{(item2.IsNullable ? string.Empty : " not")} nullable in database 2"); - } + if (item1.MaxLength != item2.MaxLength) + { + diff.Differences.Add($"has different max length - is {(item1.MaxLength.HasValue ? item1.MaxLength.Value.ToString() : "NULL")} in database 1 and is {(item2.MaxLength.HasValue ? item2.MaxLength.Value.ToString() : "NULL")} in database 2"); + } - if (item1.CollationName != item2.CollationName) - { - diff.Differences.Add($"has different collation - is {(string.IsNullOrEmpty(item1.CollationName) ? "NULL" : item1.CollationName)} in database 1 and is {(string.IsNullOrEmpty(item2.CollationName) ? "NULL" : item2.CollationName)} in database 2"); - } + if (item1.IsNullable != item2.IsNullable) + { + diff.Differences.Add($"is{(item1.IsNullable ? string.Empty : " not")} nullable in database 1 and is{(item2.IsNullable ? string.Empty : " not")} nullable in database 2"); + } - if (item1.IsAssemblyType != item2.IsAssemblyType) - { - diff.Differences.Add($"is{(item1.IsAssemblyType ? string.Empty : " not")} assembly type in database 1 and is{(item2.IsAssemblyType ? string.Empty : " not")} assembly type in database 2"); - } + if (item1.CollationName != item2.CollationName) + { + diff.Differences.Add($"has different collation - is {(string.IsNullOrEmpty(item1.CollationName) ? "NULL" : item1.CollationName)} in database 1 and is {(string.IsNullOrEmpty(item2.CollationName) ? "NULL" : item2.CollationName)} in database 2"); + } - diff.ExistsInDatabase2 = true; + if (item1.IsAssemblyType != item2.IsAssemblyType) + { + diff.Differences.Add($"is{(item1.IsAssemblyType ? string.Empty : " not")} assembly type in database 1 and is{(item2.IsAssemblyType ? string.Empty : " not")} assembly type in database 2"); } - this.Differences.UserTypeDifferences.Add(userTypeName, diff); + diff.ExistsInDatabase2 = true; } - foreach (var userType in this.Database2.UserTypes.Where(x => !this.Differences.UserTypeDifferences.ContainsKey(x.Key))) - { - this.Differences.UserTypeDifferences.Add(userType.Key, new ItemDifference(false, true)); - } + this.Differences.UserTypeDifferences.Add(userTypeName, diff); } - private void InspectSynonyms() + foreach (var userType in this.Database2.UserTypes.Where(x => !this.Differences.UserTypeDifferences.ContainsKey(x.Key))) { - foreach (var synonymName in this.Database1.Synonyms.Keys) - { - var diff = new DatabaseObjectDifference(true, false); - - var synonym = this.Database2.Synonyms.FirstOrDefault(x => x.Key == synonymName); - if (!synonym.Equals(default(KeyValuePair))) - { - if (this.Options.CompareProperties) - { - this.InspectObjectProperties(synonymName, diff); - } + this.Differences.UserTypeDifferences.Add(userType.Key, new ItemDifference(false, true)); + } + } - if (this.Options.ComparePermissions) - { - this.InspectObjectPermissions(synonymName, PermissionObjectType.Synonym, diff); - } + private void InspectSynonyms() + { + foreach (var synonymName in this.Database1.Synonyms.Keys) + { + var diff = new DatabaseObjectDifference(true, false); - diff.ObjectDefinition1 = this.Database1.Synonyms[synonymName]; - diff.ObjectDefinition2 = this.Database2.Synonyms[synonymName]; + var synonym = this.Database2.Synonyms.FirstOrDefault(x => x.Key == synonymName); + if (!synonym.Equals(default(KeyValuePair))) + { + if (this.Options.CompareProperties) + { + this.InspectObjectProperties(synonymName, diff); + } - diff.ExistsInDatabase2 = true; + if (this.Options.ComparePermissions) + { + this.InspectObjectPermissions(synonymName, PermissionObjectType.Synonym, diff); } - this.Differences.SynonymDifferences.Add(synonymName, diff); - } + diff.ObjectDefinition1 = this.Database1.Synonyms[synonymName]; + diff.ObjectDefinition2 = this.Database2.Synonyms[synonymName]; - foreach (var synonym in this.Database2.Synonyms.Where(x => !this.Differences.SynonymDifferences.ContainsKey(x.Key))) - { - this.Differences.SynonymDifferences.Add(synonym.Key, new DatabaseObjectDifference(false, true)); + diff.ExistsInDatabase2 = true; } + + this.Differences.SynonymDifferences.Add(synonymName, diff); } - private void InspectViews() + foreach (var synonym in this.Database2.Synonyms.Where(x => !this.Differences.SynonymDifferences.ContainsKey(x.Key))) { - foreach (var viewName in this.Database1.Views.Keys) - { - var diff = new DatabaseObjectDifference(true, false); + this.Differences.SynonymDifferences.Add(synonym.Key, new DatabaseObjectDifference(false, true)); + } + } - var view = this.Database2.Views.FirstOrDefault(x => x.Key == viewName); - if (!view.Equals(default(KeyValuePair))) - { - if (this.Options.CompareProperties) - { - this.InspectObjectProperties(viewName, diff); - } + private void InspectViews() + { + foreach (var viewName in this.Database1.Views.Keys) + { + var diff = new DatabaseObjectDifference(true, false); - if (this.Options.ComparePermissions) - { - this.InspectObjectPermissions(viewName, PermissionObjectType.View, diff); - } + var view = this.Database2.Views.FirstOrDefault(x => x.Key == viewName); + if (!view.Equals(default(KeyValuePair))) + { + if (this.Options.CompareProperties) + { + this.InspectObjectProperties(viewName, diff); + } - diff.ObjectDefinition1 = this.Database1.Views[viewName]; - diff.ObjectDefinition2 = view.Value; + if (this.Options.ComparePermissions) + { + this.InspectObjectPermissions(viewName, PermissionObjectType.View, diff); + } - if (diff.DefinitionsAreDifferent) - { - this.DefinitionDifferences.Add(viewName, (diff.ObjectDefinition1, diff.ObjectDefinition2)); - } + diff.ObjectDefinition1 = this.Database1.Views[viewName]; + diff.ObjectDefinition2 = view.Value; - diff.ExistsInDatabase2 = true; + if (diff.DefinitionsAreDifferent) + { + this.DefinitionDifferences.Add(viewName, (diff.ObjectDefinition1, diff.ObjectDefinition2)); } - this.Differences.ViewDifferences.Add(viewName, diff); + diff.ExistsInDatabase2 = true; } - foreach (var view in this.Database2.Views.Where(x => !this.Differences.ViewDifferences.ContainsKey(x.Key))) - { - this.Differences.ViewDifferences.Add(view.Key, new DatabaseObjectDifference(false, true)); - } + this.Differences.ViewDifferences.Add(viewName, diff); } - private void InspectRoutines() + foreach (var view in this.Database2.Views.Where(x => !this.Differences.ViewDifferences.ContainsKey(x.Key))) { - foreach (var routineName in this.Database1.UserRoutines.Keys) - { - var diff = new DatabaseObjectDifference(true, false); - var isFunction = this.Database1.UserRoutines[routineName].RoutineType.ToLower() == "function"; - - var routine = this.Database2.UserRoutines.FirstOrDefault(x => x.Key == routineName); - if (!routine.Equals(default(KeyValuePair))) - { - if (this.Options.CompareProperties) - { - this.InspectObjectProperties(routineName, diff); - } - - if (this.Options.ComparePermissions) - { - this.InspectObjectPermissions(routineName, isFunction ? PermissionObjectType.SqlFunction : PermissionObjectType.SqlStoredProcedure, diff); - } - - diff.ObjectDefinition1 = this.Database1.UserRoutines[routineName].RoutineDefinition; - diff.ObjectDefinition2 = routine.Value.RoutineDefinition; + this.Differences.ViewDifferences.Add(view.Key, new DatabaseObjectDifference(false, true)); + } + } - if (diff.DefinitionsAreDifferent) - { - this.DefinitionDifferences.Add(routineName, (diff.ObjectDefinition1, diff.ObjectDefinition2)); - } + private void InspectRoutines() + { + foreach (var routineName in this.Database1.UserRoutines.Keys) + { + var diff = new DatabaseObjectDifference(true, false); + var isFunction = this.Database1.UserRoutines[routineName].RoutineType.Equals("function", StringComparison.CurrentCultureIgnoreCase); - diff.ExistsInDatabase2 = true; + var routine = this.Database2.UserRoutines.FirstOrDefault(x => x.Key == routineName); + if (!routine.Equals(default(KeyValuePair))) + { + if (this.Options.CompareProperties) + { + this.InspectObjectProperties(routineName, diff); } - if (isFunction) + if (this.Options.ComparePermissions) { - this.Differences.FunctionDifferences.Add(routineName, diff); + this.InspectObjectPermissions(routineName, isFunction ? PermissionObjectType.SqlFunction : PermissionObjectType.SqlStoredProcedure, diff); } - else + + diff.ObjectDefinition1 = this.Database1.UserRoutines[routineName].RoutineDefinition; + diff.ObjectDefinition2 = routine.Value.RoutineDefinition; + + if (diff.DefinitionsAreDifferent) { - this.Differences.StoredProcedureDifferences.Add(routineName, diff); + this.DefinitionDifferences.Add(routineName, (diff.ObjectDefinition1, diff.ObjectDefinition2)); } + + diff.ExistsInDatabase2 = true; + } + + if (isFunction) + { + this.Differences.FunctionDifferences.Add(routineName, diff); + } + else + { + this.Differences.StoredProcedureDifferences.Add(routineName, diff); } + } - foreach (var routineName in this.Database2.UserRoutines.Keys) + foreach (var routineName in this.Database2.UserRoutines.Keys) + { + if (this.Database2.UserRoutines[routineName].RoutineType.Equals("function", StringComparison.CurrentCultureIgnoreCase)) { - if (this.Database2.UserRoutines[routineName].RoutineType.ToLower() == "function") + if (!this.Differences.FunctionDifferences.ContainsKey(routineName)) { - if (!this.Differences.FunctionDifferences.ContainsKey(routineName)) - { - this.Differences.FunctionDifferences.Add(routineName, new DatabaseObjectDifference(false, true)); - } + this.Differences.FunctionDifferences.Add(routineName, new DatabaseObjectDifference(false, true)); } - else + } + else + { + if (!this.Differences.StoredProcedureDifferences.ContainsKey(routineName)) { - if (!this.Differences.StoredProcedureDifferences.ContainsKey(routineName)) - { - this.Differences.StoredProcedureDifferences.Add(routineName, new DatabaseObjectDifference(false, true)); - } + this.Differences.StoredProcedureDifferences.Add(routineName, new DatabaseObjectDifference(false, true)); } } } + } - private void InspectObjectProperties(string fullyQualifiedObjectName, DatabaseObjectDifference objectDiff) + private void InspectObjectProperties(string fullyQualifiedObjectName, DatabaseObjectDifference objectDiff) + { + foreach (var property1 in this.Database1.ExtendedProperties) { - foreach (var property1 in this.Database1.ExtendedProperties) + var propertyObjectName = property1.ObjectName.PrependSchemaName(property1.ObjectSchema); + if (property1.Type == PropertyObjectType.Routine && propertyObjectName == fullyQualifiedObjectName) { - var propertyObjectName = property1.ObjectName.PrependSchemaName(property1.ObjectSchema); - if (property1.Type == PropertyObjectType.Routine && propertyObjectName == fullyQualifiedObjectName) - { - var diff = new ExtendedPropertyDifference(true, false); - - var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); - if (property2 != null) - { - diff.ExistsInDatabase2 = true; - diff.Value1 = property1.PropertyValue; - diff.Value2 = property2.PropertyValue; - } + var diff = new ExtendedPropertyDifference(true, false); - objectDiff.ExtendedPropertyDifferences.Add(property1.PropertyName, diff); + var property2 = this.Database2.ExtendedProperties.FirstOrDefault(x => x.FullId == property1.FullId); + if (property2 != null) + { + diff.ExistsInDatabase2 = true; + diff.Value1 = property1.PropertyValue; + diff.Value2 = property2.PropertyValue; } + + objectDiff.ExtendedPropertyDifferences.Add(property1.PropertyName, diff); } + } - foreach (var property2 in this.Database2.ExtendedProperties) + foreach (var property2 in this.Database2.ExtendedProperties) + { + var propertyObjectName = property2.ObjectName.PrependSchemaName(property2.ObjectSchema); + if (property2.Type == PropertyObjectType.Routine && + propertyObjectName == fullyQualifiedObjectName && + !objectDiff.ExtendedPropertyDifferences.ContainsKey(property2.PropertyName)) { - var propertyObjectName = property2.ObjectName.PrependSchemaName(property2.ObjectSchema); - if (property2.Type == PropertyObjectType.Routine && - propertyObjectName == fullyQualifiedObjectName && - !objectDiff.ExtendedPropertyDifferences.ContainsKey(property2.PropertyName)) - { - objectDiff.ExtendedPropertyDifferences.Add(property2.PropertyName, new ExtendedPropertyDifference(false, true)); - } + objectDiff.ExtendedPropertyDifferences.Add(property2.PropertyName, new ExtendedPropertyDifference(false, true)); } } + } - private void InspectObjectPermissions(string fullyQualifiedObjectName, PermissionObjectType objectType, DatabaseObjectDifference objectDiff) + private void InspectObjectPermissions(string fullyQualifiedObjectName, PermissionObjectType objectType, DatabaseObjectDifference objectDiff) + { + foreach (var permission1 in this.Database1.Permissions) { - foreach (var permission1 in this.Database1.Permissions) + var permissionObjectName = permission1.ObjectName.PrependSchemaName(permission1.ObjectSchema); + if (permission1.Type == objectType && permissionObjectName == fullyQualifiedObjectName) { - var permissionObjectName = permission1.ObjectName.PrependSchemaName(permission1.ObjectSchema); - if (permission1.Type == objectType && permissionObjectName == fullyQualifiedObjectName) - { - objectDiff.PermissionDifferences.Add( - permission1.ToString(), - new BaseDifference(true, this.Database2.Permissions.Exists(x => x.FullId == permission1.FullId))); - } + objectDiff.PermissionDifferences.Add( + permission1.ToString(), + new BaseDifference(true, this.Database2.Permissions.Exists(x => x.FullId == permission1.FullId))); } + } - foreach (var permission2 in this.Database2.Permissions) + foreach (var permission2 in this.Database2.Permissions) + { + var permissionObjectName = permission2.ObjectName.PrependSchemaName(permission2.ObjectSchema); + if (permission2.Type == objectType && + permissionObjectName == fullyQualifiedObjectName && + !objectDiff.PermissionDifferences.ContainsKey(permission2.ToString())) { - var permissionObjectName = permission2.ObjectName.PrependSchemaName(permission2.ObjectSchema); - if (permission2.Type == objectType && - permissionObjectName == fullyQualifiedObjectName && - !objectDiff.PermissionDifferences.ContainsKey(permission2.ToString())) - { - objectDiff.PermissionDifferences.Add(permission2.ToString(), new BaseDifference(false, true)); - } + objectDiff.PermissionDifferences.Add(permission2.ToString(), new BaseDifference(false, true)); } } } diff --git a/src/QuickCompareModel/IDifferenceBuilder.cs b/src/QuickCompareModel/IDifferenceBuilder.cs index 1bf3a51..a3395d4 100644 --- a/src/QuickCompareModel/IDifferenceBuilder.cs +++ b/src/QuickCompareModel/IDifferenceBuilder.cs @@ -2,39 +2,39 @@ // Copyright (c) Dan Ware. All rights reserved. // -namespace QuickCompareModel +namespace QuickCompareModel; + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using QuickCompareModel.DatabaseDifferences; +using QuickCompareModel.DatabaseSchema; +using QuickCompareModel.Models; + +/// +/// Class responsible for building a set of differences between two database instances. +/// +public interface IDifferenceBuilder { - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using QuickCompareModel.DatabaseDifferences; - using QuickCompareModel.DatabaseSchema; - - /// - /// Class responsible for building a set of differences between two database instances. - /// - public interface IDifferenceBuilder - { - /// Handler for when the status message changes. - event EventHandler ComparisonStatusChanged; - - /// Gets or sets the options for the comparison. - QuickCompareOptions Options { get; set; } - - /// Gets or sets the model for database 1. - SqlDatabase Database1 { get; set; } - - /// Gets or sets the model for database 2. - SqlDatabase Database2 { get; set; } - - /// Gets or sets the model representing the differences between two databases. - Differences Differences { get; set; } - - /// Gets or sets the dictionary of definitions that are different. - Dictionary DefinitionDifferences { get; set; } - - /// Inspect two database schemas and build the model. - /// A that represents the asynchronous operation. - Task BuildDifferencesAsync(); - } + /// Handler for when the status message changes. + event EventHandler ComparisonStatusChanged; + + /// Gets or sets the options for the comparison. + QuickCompareOptions Options { get; set; } + + /// Gets or sets the model for database 1. + SqlDatabase Database1 { get; set; } + + /// Gets or sets the model for database 2. + SqlDatabase Database2 { get; set; } + + /// Gets or sets the model representing the differences between two databases. + Differences Differences { get; set; } + + /// Gets or sets the dictionary of definitions that are different. + Dictionary DefinitionDifferences { get; set; } + + /// Inspect two database schemas and build the model. + /// A that represents the asynchronous operation. + Task BuildDifferencesAsync(); } \ No newline at end of file diff --git a/src/QuickCompareModel/Models/DatabaseInstance.cs b/src/QuickCompareModel/Models/DatabaseInstance.cs new file mode 100644 index 0000000..06386e5 --- /dev/null +++ b/src/QuickCompareModel/Models/DatabaseInstance.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) Dan Ware. All rights reserved. +// + +namespace QuickCompareModel.Models; + +/// Enumeration to define which database is in scope. +public enum DatabaseInstance +{ + /// Value is not defined. + Unknown, + + /// The first database in the comparison. + Database1, + + /// The second database in the comparison. + Database2, +} diff --git a/src/QuickCompareModel/Models/QuickCompareOptions.cs b/src/QuickCompareModel/Models/QuickCompareOptions.cs new file mode 100644 index 0000000..ba3d426 --- /dev/null +++ b/src/QuickCompareModel/Models/QuickCompareOptions.cs @@ -0,0 +1,81 @@ +// +// Copyright (c) Dan Ware. All rights reserved. +// + +namespace QuickCompareModel.Models; + +/// +/// Settings for the database comparison. +/// +public class QuickCompareOptions +{ + /// + /// Gets or sets the connection string for the first database. + /// + public string ConnectionString1 { get; set; } + + /// + /// Gets or sets the connection string for the second database. + /// + public string ConnectionString2 { get; set; } + + /// + /// Gets or sets a value indicating whether to ignore remarks and comments. + /// + public bool IgnoreSqlComments { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare table columns. + /// + public bool CompareColumns { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare text collation. + /// + public bool CompareCollation { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare table relations. + /// + public bool CompareRelations { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare views/functions/procedures. + /// + public bool CompareObjects { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare indexes. + /// + public bool CompareIndexes { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare permissions. + /// + public bool ComparePermissions { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare extended properties. + /// + public bool CompareProperties { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare triggers. + /// + public bool CompareTriggers { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare synonyms. + /// + public bool CompareSynonyms { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare ordinal position of columns. + /// + public bool CompareOrdinalPositions { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to compare user types. + /// + public bool CompareUserTypes { get; set; } = true; +} diff --git a/src/QuickCompareModel/Models/StatusChangedEventArgs.cs b/src/QuickCompareModel/Models/StatusChangedEventArgs.cs new file mode 100644 index 0000000..8072ab6 --- /dev/null +++ b/src/QuickCompareModel/Models/StatusChangedEventArgs.cs @@ -0,0 +1,27 @@ +// +// Copyright (c) Dan Ware. All rights reserved. +// + +namespace QuickCompareModel.Models; + +using System; + +/// Custom derivative of to contain a status message. +/// Current status message. +[Serializable] +public class StatusChangedEventArgs(string statusMessage) + : EventArgs +{ + /// Initialises a new instance of the class. + /// Current status message. + /// The database in scope. + public StatusChangedEventArgs(string statusMessage, DatabaseInstance databaseInstance) + : this(statusMessage) => this.DatabaseInstance = databaseInstance; + + /// Gets or sets the current status message. + public string StatusMessage { get; set; } = statusMessage; + + /// Gets or sets the database in scope. + /// Default value is . + public DatabaseInstance DatabaseInstance { get; set; } = DatabaseInstance.Unknown; +} diff --git a/src/QuickCompareModel/QuickCompareModel.csproj b/src/QuickCompareModel/QuickCompareModel.csproj index 3642737..93c4441 100644 --- a/src/QuickCompareModel/QuickCompareModel.csproj +++ b/src/QuickCompareModel/QuickCompareModel.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net8.0 Dan Ware QuickCompare database compare;database comparison;database schema;schema checker;schema compare;schema comparison;database schema compare;database schema comparison @@ -25,7 +25,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/QuickCompareModel/QuickCompareOptions.cs b/src/QuickCompareModel/QuickCompareOptions.cs deleted file mode 100644 index 17a1fa1..0000000 --- a/src/QuickCompareModel/QuickCompareOptions.cs +++ /dev/null @@ -1,82 +0,0 @@ -// -// Copyright (c) Dan Ware. All rights reserved. -// - -namespace QuickCompareModel -{ - /// - /// Settings for the database comparison. - /// - public class QuickCompareOptions - { - /// - /// Gets or sets the connection string for the first database. - /// - public string ConnectionString1 { get; set; } - - /// - /// Gets or sets the connection string for the second database. - /// - public string ConnectionString2 { get; set; } - - /// - /// Gets or sets a value indicating whether to ignore remarks and comments. - /// - public bool IgnoreSqlComments { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare table columns. - /// - public bool CompareColumns { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare text collation. - /// - public bool CompareCollation { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare table relations. - /// - public bool CompareRelations { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare views/functions/procedures. - /// - public bool CompareObjects { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare indexes. - /// - public bool CompareIndexes { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare permissions. - /// - public bool ComparePermissions { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare extended properties. - /// - public bool CompareProperties { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare triggers. - /// - public bool CompareTriggers { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare synonyms. - /// - public bool CompareSynonyms { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare ordinal position of columns. - /// - public bool CompareOrdinalPositions { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to compare user types. - /// - public bool CompareUserTypes { get; set; } = true; - } -} diff --git a/src/QuickCompareModel/StatusChangedEventArgs.cs b/src/QuickCompareModel/StatusChangedEventArgs.cs deleted file mode 100644 index 3113638..0000000 --- a/src/QuickCompareModel/StatusChangedEventArgs.cs +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) Dan Ware. All rights reserved. -// - -namespace QuickCompareModel -{ - using System; - - /// Custom derivative of to contain a status message. - [Serializable] - public class StatusChangedEventArgs : EventArgs - { - /// Initialises a new instance of the class. - /// Current status message. - public StatusChangedEventArgs(string statusMessage) => this.StatusMessage = statusMessage; - - /// Initialises a new instance of the class. - /// Current status message. - /// The database in scope. - public StatusChangedEventArgs(string statusMessage, DatabaseInstance databaseInstance) - : this(statusMessage) => this.DatabaseInstance = databaseInstance; - - /// Gets or sets the current status message. - public string StatusMessage { get; set; } - - /// Gets or sets the database in scope. - /// Default value is . - public DatabaseInstance DatabaseInstance { get; set; } = DatabaseInstance.Unknown; - } -} diff --git a/src/WinQuickCompare/App.xaml.cs b/src/WinQuickCompare/App.xaml.cs index a1f3829..b9c57b0 100644 --- a/src/WinQuickCompare/App.xaml.cs +++ b/src/WinQuickCompare/App.xaml.cs @@ -40,8 +40,8 @@ private void AppDomain_UnhandledException(object sender, UnhandledExceptionEvent private void ReadApplicationPropertiesFromFile() { IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForDomain(); - using IsolatedStorageFileStream stream = new (Filename, FileMode.OpenOrCreate, storage); - using StreamReader reader = new (stream); + using IsolatedStorageFileStream stream = new(Filename, FileMode.OpenOrCreate, storage); + using StreamReader reader = new(stream); while (!reader.EndOfStream) { this.SetPropertyFromLineOfText(reader.ReadLine()); @@ -51,15 +51,15 @@ private void ReadApplicationPropertiesFromFile() private void SetPropertyFromLineOfText(string line) { string key = line[..line.IndexOf(',')]; - string value = line[(line.IndexOf(',') + 1) ..]; + string value = line[(line.IndexOf(',') + 1)..]; this.Properties[key] = value; } private void WriteApplicationPropertiesToFile() { using IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForDomain(); - using IsolatedStorageFileStream stream = new (Filename, FileMode.Create, storage); - using StreamWriter writer = new (stream); + using IsolatedStorageFileStream stream = new(Filename, FileMode.Create, storage); + using StreamWriter writer = new(stream); foreach (string key in this.Properties.Keys) { writer.WriteLine("{0},{1}", key, this.Properties[key]); diff --git a/src/WinQuickCompare/MainWindow.xaml.cs b/src/WinQuickCompare/MainWindow.xaml.cs index ebabe2b..32362aa 100644 --- a/src/WinQuickCompare/MainWindow.xaml.cs +++ b/src/WinQuickCompare/MainWindow.xaml.cs @@ -8,6 +8,7 @@ namespace QuickCompare; using System.Windows; using System.Windows.Controls; using QuickCompareModel; +using QuickCompareModel.Models; /// Interaction logic for the main window. public partial class MainWindow : Window @@ -23,7 +24,7 @@ private async void ButtonRunComparison_Click(object sender, RoutedEventArgs e) try { var context = (QuickCompareContext)this.FindResource(nameof(QuickCompareContext)); - DifferenceBuilder builder = new (context.OptionsFromProperties()); + DifferenceBuilder builder = new(context.OptionsFromProperties()); builder.ComparisonStatusChanged += this.Comparison_StatusChanged; await builder.BuildDifferencesAsync(); diff --git a/src/WinQuickCompare/QuickCompareContext.cs b/src/WinQuickCompare/QuickCompareContext.cs index 5aed827..311a769 100644 --- a/src/WinQuickCompare/QuickCompareContext.cs +++ b/src/WinQuickCompare/QuickCompareContext.cs @@ -7,7 +7,7 @@ namespace QuickCompare; using System.ComponentModel; using System.Windows; using Microsoft.Extensions.Options; -using QuickCompareModel; +using QuickCompareModel.Models; /// /// View model class which uses application properties to persist form input between restarts. @@ -112,7 +112,7 @@ public bool CompareUserTypes /// Instance of . public IOptions OptionsFromProperties() { - QuickCompareOptions settings = new () + QuickCompareOptions settings = new() { ConnectionString1 = this.ConnectionString1, ConnectionString2 = this.ConnectionString2, @@ -138,7 +138,7 @@ protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { - PropertyChangedEventArgs e = new (propertyName); + PropertyChangedEventArgs e = new(propertyName); this.PropertyChanged(this, e); } } diff --git a/src/WinQuickCompare/WinQuickCompare.csproj b/src/WinQuickCompare/WinQuickCompare.csproj index 55efdd9..c2c1e06 100644 --- a/src/WinQuickCompare/WinQuickCompare.csproj +++ b/src/WinQuickCompare/WinQuickCompare.csproj @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/QuickCompareTests/BaseDifferenceTests.cs b/test/QuickCompareTests/BaseDifferenceTests.cs index 2489a94..e9e112f 100644 --- a/test/QuickCompareTests/BaseDifferenceTests.cs +++ b/test/QuickCompareTests/BaseDifferenceTests.cs @@ -29,7 +29,8 @@ public void CleanDefinitionText_Strips_SingleLineComments() => "-- Line comment\r\n" + "SELECT * -- Inline comment\r\n" + "FROM Table\r\n" + - "-- Line comment", DoNotStripWhitespace) + "-- Line comment", + DoNotStripWhitespace) .Should().Be("SELECT * \r\nFROM Table"); /// Test multi-line comments are stripped. @@ -38,7 +39,8 @@ public void CleanDefinitionText_Strips_MultiLineComments() => BaseDifference.CleanDefinitionText( "/******************\r\n" + " ** Comment body **\r\n" + - " ******************/ SELECT * FROM Table", DoNotStripWhitespace) + " ******************/ SELECT * FROM Table", + DoNotStripWhitespace) .Should().Be("SELECT * FROM Table"); /// Test enclosed comments are stripped. @@ -53,7 +55,8 @@ public void CleanDefinitionText_Strips_EnclosedComments() => public void CleanDefinitionText_Normalises_CommaWhitespace() => BaseDifference.CleanDefinitionText( "SELECT ID , Name ,Email, Phone , FootSize\r" + - ",Timestamp FROM Table", StripWhitespace) + ",Timestamp FROM Table", + StripWhitespace) .Should().Be("SELECT ID, Name, Email, Phone, FootSize, Timestamp FROM Table"); /// Test whitespace gets condensed to a single space. diff --git a/test/QuickCompareTests/QuickCompareTests.csproj b/test/QuickCompareTests/QuickCompareTests.csproj index c47cf41..527a149 100644 --- a/test/QuickCompareTests/QuickCompareTests.csproj +++ b/test/QuickCompareTests/QuickCompareTests.csproj @@ -13,7 +13,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/QuickCompareTests/RelationCompareTests.cs b/test/QuickCompareTests/RelationCompareTests.cs index aebb219..dcb26d3 100644 --- a/test/QuickCompareTests/RelationCompareTests.cs +++ b/test/QuickCompareTests/RelationCompareTests.cs @@ -4,6 +4,7 @@ namespace QuickCompareTests; +using System.Threading.Tasks; using FluentAssertions; using QuickCompareModel.DatabaseSchema; using Xunit; @@ -17,8 +18,9 @@ public class RelationCompareTests private const string TableName = "[dbo].[Table1]"; /// Test relation difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void RelationMissingFromDatabase1_IsReported() + public async Task RelationMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -28,7 +30,7 @@ public void RelationMissingFromDatabase1_IsReported() builder.Database2.Tables[TableName].Relations.Add(new SqlRelation { RelationName = RelationName }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences[TableName] @@ -43,8 +45,9 @@ public void RelationMissingFromDatabase1_IsReported() } /// Test relation difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void RelationMissingFromDatabase2_IsReported() + public async Task RelationMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -54,7 +57,7 @@ public void RelationMissingFromDatabase2_IsReported() builder.Database2.Tables.Add(TableName, new SqlTable()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences[TableName] @@ -69,8 +72,9 @@ public void RelationMissingFromDatabase2_IsReported() } /// Test relation difference is not reported. + /// A representing the asynchronous unit test. [Fact] - public void RelationsInBothDatabases_AreNotReported() + public async Task RelationsInBothDatabases_AreNotReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -81,7 +85,7 @@ public void RelationsInBothDatabases_AreNotReported() builder.Database2.Tables[TableName].Relations.Add(new SqlRelation { RelationName = RelationName }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences[TableName] diff --git a/test/QuickCompareTests/StoredProcedureCompareTests.cs b/test/QuickCompareTests/StoredProcedureCompareTests.cs index 5d354db..c838fd8 100644 --- a/test/QuickCompareTests/StoredProcedureCompareTests.cs +++ b/test/QuickCompareTests/StoredProcedureCompareTests.cs @@ -4,6 +4,7 @@ namespace QuickCompareTests; +using System.Threading.Tasks; using FluentAssertions; using QuickCompareModel.DatabaseSchema; using Xunit; @@ -17,15 +18,16 @@ public class StoredProcedureCompareTests private const string StoredProcedureRoutineType = "PROCEDURE"; /// Test stored procedure difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void StoredProcedureMissingFromDatabase1_IsReported() + public async Task StoredProcedureMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); builder.Database2.UserRoutines.Add(StoredProcedureName, new SqlUserRoutine { RoutineType = StoredProcedureRoutineType }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.StoredProcedureDifferences.Should().ContainKey(StoredProcedureName); @@ -37,15 +39,16 @@ public void StoredProcedureMissingFromDatabase1_IsReported() } /// Test stored procedure difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void StoredProcedureMissingFromDatabase2_IsReported() + public async Task StoredProcedureMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); builder.Database1.UserRoutines.Add(StoredProcedureName, new SqlUserRoutine { RoutineType = StoredProcedureRoutineType }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.StoredProcedureDifferences.Should().ContainKey(StoredProcedureName); @@ -57,8 +60,9 @@ public void StoredProcedureMissingFromDatabase2_IsReported() } /// Test stored procedure difference is not reported. + /// A representing the asynchronous unit test. [Fact] - public void StoredProceduresInBothDatabases_AreNotReported() + public async Task StoredProceduresInBothDatabases_AreNotReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -66,7 +70,7 @@ public void StoredProceduresInBothDatabases_AreNotReported() builder.Database2.UserRoutines.Add(StoredProcedureName, new SqlUserRoutine { RoutineType = StoredProcedureRoutineType }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.StoredProcedureDifferences.Should().ContainKey(StoredProcedureName); @@ -77,8 +81,9 @@ public void StoredProceduresInBothDatabases_AreNotReported() } /// Test stored procedure difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void StoredProcedureDefinitionDifference_IsReported() + public async Task StoredProcedureDefinitionDifference_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -86,7 +91,7 @@ public void StoredProcedureDefinitionDifference_IsReported() builder.Database2.UserRoutines.Add(StoredProcedureName, new SqlUserRoutine { RoutineType = StoredProcedureRoutineType, RoutineDefinition = "bar" }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.StoredProcedureDifferences.Should().ContainKey(StoredProcedureName); @@ -97,8 +102,9 @@ public void StoredProcedureDefinitionDifference_IsReported() } /// Test stored procedure difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void StoredProcedurePropertyMissingFromDatabase1_IsReported() + public async Task StoredProcedurePropertyMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -115,7 +121,7 @@ public void StoredProcedurePropertyMissingFromDatabase1_IsReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.StoredProcedureDifferences.Should().ContainKey(StoredProcedureName); @@ -127,8 +133,9 @@ public void StoredProcedurePropertyMissingFromDatabase1_IsReported() } /// Test stored procedure difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void StoredProcedurePropertyMissingFromDatabase2_IsReported() + public async Task StoredProcedurePropertyMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -145,7 +152,7 @@ public void StoredProcedurePropertyMissingFromDatabase2_IsReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.StoredProcedureDifferences.Should().ContainKey(StoredProcedureName); @@ -157,8 +164,9 @@ public void StoredProcedurePropertyMissingFromDatabase2_IsReported() } /// Test stored procedure difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void StoredProcedurePropertyDifference_IsReported() + public async Task StoredProcedurePropertyDifference_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -184,7 +192,7 @@ public void StoredProcedurePropertyDifference_IsReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.StoredProcedureDifferences.Should().ContainKey(StoredProcedureName); diff --git a/test/QuickCompareTests/SynonymCompareTests.cs b/test/QuickCompareTests/SynonymCompareTests.cs index 6e4875a..757f858 100644 --- a/test/QuickCompareTests/SynonymCompareTests.cs +++ b/test/QuickCompareTests/SynonymCompareTests.cs @@ -4,6 +4,7 @@ namespace QuickCompareTests; +using System.Threading.Tasks; using FluentAssertions; using Xunit; @@ -13,8 +14,9 @@ namespace QuickCompareTests; public class SynonymCompareTests { /// Test synonym difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void SynonymMissingFromDatabase1_IsReported() + public async Task SynonymMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -23,7 +25,7 @@ public void SynonymMissingFromDatabase1_IsReported() builder.Database2.Synonyms.Add(synonymName, "foobar"); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.SynonymDifferences.Should().ContainKey(synonymName); @@ -35,8 +37,9 @@ public void SynonymMissingFromDatabase1_IsReported() } /// Test synonym difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void SynonymMissingFromDatabase2_IsReported() + public async Task SynonymMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -45,7 +48,7 @@ public void SynonymMissingFromDatabase2_IsReported() builder.Database1.Synonyms.Add(synonymName, "foobar"); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.SynonymDifferences.Should().ContainKey(synonymName); @@ -57,8 +60,9 @@ public void SynonymMissingFromDatabase2_IsReported() } /// Test synonym difference is not reported. + /// A representing the asynchronous unit test. [Fact] - public void SynonymsInBothDatabases_AreNotReported() + public async Task SynonymsInBothDatabases_AreNotReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -68,7 +72,7 @@ public void SynonymsInBothDatabases_AreNotReported() builder.Database2.Synonyms.Add(synonymName, "foobar"); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.SynonymDifferences.Should().ContainKey(synonymName); diff --git a/test/QuickCompareTests/TableCompareTests.cs b/test/QuickCompareTests/TableCompareTests.cs index 613872b..ad6721b 100644 --- a/test/QuickCompareTests/TableCompareTests.cs +++ b/test/QuickCompareTests/TableCompareTests.cs @@ -5,6 +5,7 @@ namespace QuickCompareTests; using System.Collections.Generic; +using System.Threading.Tasks; using FluentAssertions; using QuickCompareModel.DatabaseSchema; using Xunit; @@ -18,15 +19,16 @@ public class TableCompareTests private const string TabIndent = " "; /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TableMissingFromDatabase1_IsReported() + public async Task TableMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); builder.Database2.Tables.Add(TableName, new SqlTable()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -38,15 +40,16 @@ public void TableMissingFromDatabase1_IsReported() } /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TableMissingFromDatabase2_IsReported() + public async Task TableMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); builder.Database1.Tables.Add(TableName, new SqlTable()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -58,8 +61,9 @@ public void TableMissingFromDatabase2_IsReported() } /// Test table difference is not reported. + /// A representing the asynchronous unit test. [Fact] - public void TablesInBothDatabases_AreNotReported() + public async Task TablesInBothDatabases_AreNotReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -67,7 +71,7 @@ public void TablesInBothDatabases_AreNotReported() builder.Database2.Tables.Add(TableName, new SqlTable()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -80,8 +84,9 @@ public void TablesInBothDatabases_AreNotReported() } /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TablePropertyMissingFromDatabase1_IsReported() + public async Task TablePropertyMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -99,7 +104,7 @@ public void TablePropertyMissingFromDatabase1_IsReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -117,8 +122,9 @@ public void TablePropertyMissingFromDatabase1_IsReported() } /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TablePropertyMissingFromDatabase2_IsReported() + public async Task TablePropertyMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -136,7 +142,7 @@ public void TablePropertyMissingFromDatabase2_IsReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -155,8 +161,9 @@ public void TablePropertyMissingFromDatabase2_IsReported() } /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TablePropertyDifference_IsReported() + public async Task TablePropertyDifference_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -184,7 +191,7 @@ public void TablePropertyDifference_IsReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -201,8 +208,9 @@ public void TablePropertyDifference_IsReported() } /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TablePermissionMissingFromDatabase1_IsReported() + public async Task TablePermissionMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -219,7 +227,7 @@ public void TablePermissionMissingFromDatabase1_IsReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -235,8 +243,9 @@ public void TablePermissionMissingFromDatabase1_IsReported() } /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TablePermissionMissingFromDatabase2_IsReported() + public async Task TablePermissionMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -253,7 +262,7 @@ public void TablePermissionMissingFromDatabase2_IsReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -269,8 +278,9 @@ public void TablePermissionMissingFromDatabase2_IsReported() } /// Test table difference is not reported. + /// A representing the asynchronous unit test. [Fact] - public void TablePermissionInBothDatabases_IsNotReported() + public async Task TablePermissionInBothDatabases_IsNotReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -296,7 +306,7 @@ public void TablePermissionInBothDatabases_IsNotReported() }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -314,17 +324,18 @@ public void TablePermissionInBothDatabases_IsNotReported() } /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TableColumn_WithSingleDifference_IsSingleLine() + public async Task TableColumn_WithSingleDifference_IsSingleLine() { // Arrange var builder = TestHelper.GetBasicBuilder(); - builder.Database1.Tables.Add(TableName, new SqlTable { ColumnDetails = new List { new SqlColumnDetail { ColumnName = "foobar" } } }); + builder.Database1.Tables.Add(TableName, new SqlTable { ColumnDetails = [new SqlColumnDetail { ColumnName = "foobar" }] }); builder.Database1.Tables[TableName].ColumnDetails[0].OrdinalPosition = 1; - builder.Database2.Tables.Add(TableName, new SqlTable { ColumnDetails = new List { new SqlColumnDetail { ColumnName = "foobar" } } }); + builder.Database2.Tables.Add(TableName, new SqlTable { ColumnDetails = [new SqlColumnDetail { ColumnName = "foobar" }] }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); @@ -337,18 +348,19 @@ public void TableColumn_WithSingleDifference_IsSingleLine() } /// Test table difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TableColumn_WithMultipleDifferences_IsMultipleLines() + public async Task TableColumn_WithMultipleDifferences_IsMultipleLines() { // Arrange var builder = TestHelper.GetBasicBuilder(); - builder.Database1.Tables.Add(TableName, new SqlTable { ColumnDetails = new List { new SqlColumnDetail { ColumnName = "foobar" } } }); + builder.Database1.Tables.Add(TableName, new SqlTable { ColumnDetails = [new SqlColumnDetail { ColumnName = "foobar" }] }); builder.Database1.Tables[TableName].ColumnDetails[0].OrdinalPosition = 1; builder.Database1.Tables[TableName].ColumnDetails[0].NumericPrecision = 1; - builder.Database2.Tables.Add(TableName, new SqlTable { ColumnDetails = new List { new SqlColumnDetail { ColumnName = "foobar" } } }); + builder.Database2.Tables.Add(TableName, new SqlTable { ColumnDetails = [new SqlColumnDetail { ColumnName = "foobar" }] }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences.Should().ContainKey(TableName); diff --git a/test/QuickCompareTests/TestHelper.cs b/test/QuickCompareTests/TestHelper.cs index a9883a4..d83e1de 100644 --- a/test/QuickCompareTests/TestHelper.cs +++ b/test/QuickCompareTests/TestHelper.cs @@ -7,6 +7,7 @@ namespace QuickCompareTests; using Microsoft.Extensions.Options; using QuickCompareModel; using QuickCompareModel.DatabaseSchema; +using QuickCompareModel.Models; /// /// Class to provide common setup methods for tests. diff --git a/test/QuickCompareTests/TriggerCompareTests.cs b/test/QuickCompareTests/TriggerCompareTests.cs index 30b7e60..66b74e1 100644 --- a/test/QuickCompareTests/TriggerCompareTests.cs +++ b/test/QuickCompareTests/TriggerCompareTests.cs @@ -4,6 +4,7 @@ namespace QuickCompareTests; +using System.Threading.Tasks; using FluentAssertions; using QuickCompareModel.DatabaseSchema; using Xunit; @@ -14,8 +15,9 @@ namespace QuickCompareTests; public class TriggerCompareTests { /// Test trigger difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TriggerMissingFromDatabase1_IsReported() + public async Task TriggerMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -27,7 +29,7 @@ public void TriggerMissingFromDatabase1_IsReported() builder.Database2.Tables[tableName].Triggers.Add(new SqlTrigger { TriggerName = triggerName }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences[tableName] @@ -42,8 +44,9 @@ public void TriggerMissingFromDatabase1_IsReported() } /// Test trigger difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void TriggerMissingFromDatabase2_IsReported() + public async Task TriggerMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -55,7 +58,7 @@ public void TriggerMissingFromDatabase2_IsReported() builder.Database2.Tables.Add(tableName, new SqlTable()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences[tableName] @@ -70,8 +73,9 @@ public void TriggerMissingFromDatabase2_IsReported() } /// Test trigger difference is not reported. + /// A representing the asynchronous unit test. [Fact] - public void TriggerInBothDatabases_IsNotReported() + public async Task TriggerInBothDatabases_IsNotReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -84,7 +88,7 @@ public void TriggerInBothDatabases_IsNotReported() builder.Database2.Tables[tableName].Triggers.Add(new SqlTrigger { TriggerName = triggerName, TableName = tableName }); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.TableDifferences[tableName] diff --git a/test/QuickCompareTests/UserTypeCompareTests.cs b/test/QuickCompareTests/UserTypeCompareTests.cs index 7061144..7379651 100644 --- a/test/QuickCompareTests/UserTypeCompareTests.cs +++ b/test/QuickCompareTests/UserTypeCompareTests.cs @@ -4,6 +4,7 @@ namespace QuickCompareTests; +using System.Threading.Tasks; using FluentAssertions; using QuickCompareModel.DatabaseSchema; using Xunit; @@ -14,8 +15,9 @@ namespace QuickCompareTests; public class UserTypeCompareTests { /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void UserTypeMissingFromDatabase1_IsReported() + public async Task UserTypeMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -24,7 +26,7 @@ public void UserTypeMissingFromDatabase1_IsReported() builder.Database2.UserTypes.Add(userTypeName, new SqlUserType()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.UserTypeDifferences.Should().ContainKey(userTypeName); @@ -36,8 +38,9 @@ public void UserTypeMissingFromDatabase1_IsReported() } /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void UserTypeMissingFromDatabase2_IsReported() + public async Task UserTypeMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -46,7 +49,7 @@ public void UserTypeMissingFromDatabase2_IsReported() builder.Database1.UserTypes.Add(userTypeName, new SqlUserType()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.UserTypeDifferences.Should().ContainKey(userTypeName); @@ -58,8 +61,9 @@ public void UserTypeMissingFromDatabase2_IsReported() } /// Test user type difference is not reported. + /// A representing the asynchronous unit test. [Fact] - public void UserTypeInBothDatabases_IsNotReported() + public async Task UserTypeInBothDatabases_IsNotReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -69,7 +73,7 @@ public void UserTypeInBothDatabases_IsNotReported() builder.Database2.UserTypes.Add(userTypeName, new SqlUserType()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.UserTypeDifferences.Should().ContainKey(userTypeName); @@ -81,48 +85,69 @@ public void UserTypeInBothDatabases_IsNotReported() } /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void UnderlyingTypeName_Difference_IsReported() => - ComparisonResultContainsValue(new SqlUserType { UnderlyingTypeName = "char" }, "underlying type") - .Should().BeTrue(); + public async Task UnderlyingTypeName_Difference_IsReported() + { + var result = await ComparisonResultContainsValue(new SqlUserType { UnderlyingTypeName = "char" }, "underlying type"); + result.Should().BeTrue(); + } /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void Precision_Difference_IsReported() => - ComparisonResultContainsValue(new SqlUserType { Precision = 2 }, "precision") - .Should().BeTrue(); + public async Task Precision_Difference_IsReported() + { + var result = await ComparisonResultContainsValue(new SqlUserType { Precision = 2 }, "precision"); + result.Should().BeTrue(); + } /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void Scale_Difference_IsReported() => - ComparisonResultContainsValue(new SqlUserType { Scale = 2 }, "scale") - .Should().BeTrue(); + public async Task Scale_Difference_IsReported() + { + var result = await ComparisonResultContainsValue(new SqlUserType { Scale = 2 }, "scale"); + result.Should().BeTrue(); + } /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void MaxLength_Difference_IsReported() => - ComparisonResultContainsValue(new SqlUserType { MaxLength = 2 }, "max length") - .Should().BeTrue(); + public async Task MaxLength_Difference_IsReported() + { + var result = await ComparisonResultContainsValue(new SqlUserType { MaxLength = 2 }, "max length"); + result.Should().BeTrue(); + } /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void IsNullable_Difference_IsReported() => - ComparisonResultContainsValue(new SqlUserType { IsNullable = true }, "nullable") - .Should().BeTrue(); + public async Task IsNullable_Difference_IsReported() + { + var result = await ComparisonResultContainsValue(new SqlUserType { IsNullable = true }, "nullable"); + result.Should().BeTrue(); + } /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void CollationName_Difference_IsReported() => - ComparisonResultContainsValue(new SqlUserType { CollationName = "foobar" }, "collation") - .Should().BeTrue(); + public async Task CollationName_Difference_IsReported() + { + var result = await ComparisonResultContainsValue(new SqlUserType { CollationName = "foobar" }, "collation"); + result.Should().BeTrue(); + } /// Test user type difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void IsAssemblyType_Difference_IsReported() => - ComparisonResultContainsValue(new SqlUserType { IsAssemblyType = true }, "assembly") - .Should().BeTrue(); + public async Task IsAssemblyType_Difference_IsReported() + { + var result = await ComparisonResultContainsValue(new SqlUserType { IsAssemblyType = true }, "assembly"); + result.Should().BeTrue(); + } - private static bool ComparisonResultContainsValue(SqlUserType userType, string value) + private static async Task ComparisonResultContainsValue(SqlUserType userType, string value) { // Arrange var userTypeName = "Type1"; @@ -132,7 +157,7 @@ private static bool ComparisonResultContainsValue(SqlUserType userType, string v builder.Database2.UserTypes.Add(userTypeName, new SqlUserType()); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert var diff = builder.Differences.UserTypeDifferences[userTypeName]; diff --git a/test/QuickCompareTests/ViewCompareTests.cs b/test/QuickCompareTests/ViewCompareTests.cs index a27b5b1..70848ef 100644 --- a/test/QuickCompareTests/ViewCompareTests.cs +++ b/test/QuickCompareTests/ViewCompareTests.cs @@ -4,6 +4,7 @@ namespace QuickCompareTests; +using System.Threading.Tasks; using FluentAssertions; using Xunit; @@ -13,8 +14,9 @@ namespace QuickCompareTests; public class ViewCompareTests { /// Test view difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void ViewMissingFromDatabase1_IsReported() + public async Task ViewMissingFromDatabase1_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -23,7 +25,7 @@ public void ViewMissingFromDatabase1_IsReported() builder.Database2.Views.Add(viewName, "foobar"); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.ViewDifferences.Should().ContainKey(viewName); @@ -35,8 +37,9 @@ public void ViewMissingFromDatabase1_IsReported() } /// Test view difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void ViewMissingFromDatabase2_IsReported() + public async Task ViewMissingFromDatabase2_IsReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -45,7 +48,7 @@ public void ViewMissingFromDatabase2_IsReported() builder.Database1.Views.Add(viewName, "foobar"); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.ViewDifferences.Should().ContainKey(viewName); @@ -57,8 +60,9 @@ public void ViewMissingFromDatabase2_IsReported() } /// Test view difference is reported. + /// A representing the asynchronous unit test. [Fact] - public void ViewsInBothDatabases_AreNotReported() + public async Task ViewsInBothDatabases_AreNotReported() { // Arrange var builder = TestHelper.GetBasicBuilder(); @@ -68,7 +72,7 @@ public void ViewsInBothDatabases_AreNotReported() builder.Database2.Views.Add(viewName, "foobar"); // Act - builder.BuildDifferencesAsync().Wait(); + await builder.BuildDifferencesAsync(); // Assert builder.Differences.ViewDifferences.Should().ContainKey(viewName);