### Install CompareNETObjects NuGet Package Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Use the NuGet Package Manager Console or the .NET CLI to install the CompareNETObjects library. ```bash # NuGet Package Manager Console Install-Package CompareNETObjects ``` ```bash # .NET CLI dotnet add package CompareNETObjects ``` -------------------------------- ### Install CompareNETObjects via .NET CLI Source: https://github.com/gregfinzer/compare-net-objects/wiki/Getting-Started Use this command in the .NET CLI to add the library to your project. ```bash dotnet add package CompareNETObjects ``` -------------------------------- ### Install CompareNETObjects via NuGet Package Manager Console Source: https://github.com/gregfinzer/compare-net-objects/wiki/Getting-Started Use this command in the Package Manager Console to install the library. ```powershell Install-Package CompareNETObjects ``` -------------------------------- ### Pass Custom Configuration Object Source: https://github.com/gregfinzer/compare-net-objects/wiki/Configuration Instantiate CompareLogic with a pre-configured ComparisonConfig object. This allows for more complex or reusable configuration setups. ```csharp ComparisonConfig config = new ComparisonConfig(); config.MaxDifferences = 100; CompareLogic logic = new CompareLogic(config); ``` -------------------------------- ### Generate and Launch WinMerge Report Source: https://github.com/gregfinzer/compare-net-objects/wiki/WinMerge-Report This C# test case demonstrates how to create two .NET objects, compare them using CompareLogic, generate difference files using WinMergeReport, and then launch WinMerge to view the differences. Ensure WinMerge is installed and its executable path is correctly configured. ```csharp string expected = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "winMergeExpected.txt"); string actual = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "winMergeActual.txt"); if (File.Exists(expected)) File.Delete(expected); if (File.Exists(actual)) File.Delete(actual); Movie winMergeExpected = new Movie(); winMergeExpected.Name = "Oblivion"; winMergeExpected.PaymentForTomCruise = 2000000M; Movie winMergeActual = new Movie(); winMergeActual.Name = "Edge of Tomorrow"; winMergeActual.PaymentForTomCruise = 3000000M; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = Int32.MaxValue; ComparisonResult result = compareLogic.Compare(winMergeExpected, winMergeActual); WinMergeReport winMergeReport = new WinMergeReport(); winMergeReport.OutputFiles(result.Differences, expected, actual); Assert.IsTrue(File.Exists(expected)); Assert.IsTrue(File.Exists(actual)); if (!string.IsNullOrEmpty(winMergeReport.FindWinMerge())) winMergeReport.LaunchApplication(expected,actual); ``` -------------------------------- ### Launch Diffs in Beyond Compare Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Writes two side-by-side text files and optionally launches the Beyond Compare external diff tool. Ensure Beyond Compare is installed for the launch functionality. ```csharp using KellermanSoftware.CompareNetObjects; using KellermanSoftware.CompareNetObjects.Reports; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = int.MaxValue; ComparisonResult result = compareLogic.Compare(expectedObj, actualObj); BeyondCompareReport report = new BeyondCompareReport(); string expectedFile = "beyondExpected.txt"; string actualFile = "beyondActual.txt"; report.OutputFiles(result.Differences, expectedFile, actualFile); // Only launch if Beyond Compare is installed if (!string.IsNullOrEmpty(report.FindBeyondCompare())) report.LaunchApplication(expectedFile, actualFile); ``` -------------------------------- ### BDD Assertion with Custom Comparison Configuration Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Perform BDD-style assertions using a custom comparison configuration. This example demonstrates case-insensitive comparison for a Product's Name property. ```csharp using KellermanSoftware.CompareNetObjects; [Test] public void CompareWithCustomConfig() { var config = new ComparisonConfig { CaseSensitive = false }; var a = new Product { Name = "Widget" }; var b = new Product { Name = "WIDGET" }; a.ShouldCompare(b, compareConfig: config); // passes — case insensitive } ``` -------------------------------- ### ShouldCompare Extension Example Source: https://github.com/gregfinzer/compare-net-objects/wiki/Test-Extensions Use ShouldCompare to assert that two objects are equal. An optional error message can be provided. ```csharp [Test] public void ShouldCompare_When_Equal_Should__Not_Throw_An_Exception() { //Arrange string errorMessage = "Groups should be equal"; var people1 = new List() { new Person() { Name = "Joe" } }; var people2 = new List() { new Person() { Name = "Joe" } }; var group1 = new KeyValuePair>("People", people1); var group2 = new KeyValuePair>("People", people2); //Assert group1.ShouldCompare(group2, errorMessage); } ``` -------------------------------- ### ShouldNotCompare Extension Example Source: https://github.com/gregfinzer/compare-net-objects/wiki/Test-Extensions Use ShouldNotCompare to assert that two objects are not equal. An optional error message can be provided. ```csharp [Test] public void ShouldNotCompare_When_Not_Equal_Should__Not_Throw_An_Exception() { //Arrange string errorMessage = "Groups should be equal"; var people1 = new List() { new Person() { Name = "Joe" } }; var people2 = new List() { new Person() { Name = "Sue" } }; var group1 = new KeyValuePair>("People", people1); var group2 = new KeyValuePair>("People", people2); //Act group1.ShouldNotCompare(group2, errorMessage); } ``` -------------------------------- ### Custom Comparer for Tenant Type Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Subclass BaseTypeComparer to implement complex comparison rules for a specific type. This example defines custom logic for comparing Tenant objects based on Name and Amount. ```csharp public class TenantComparer : BaseTypeComparer { public TenantComparer(RootComparer rootComparer) : base(rootComparer) { } public override bool IsTypeMatch(Type type1, Type type2) => type1 == typeof(Tenant) && type2 == typeof(Tenant); public override void CompareType(CompareParms parms) { var t1 = (Tenant)parms.Object1; var t2 = (Tenant)parms.Object2; // Only flag as different if names differ AND amount is outside [100, 200] if (t1.Name != t2.Name || t1.Amount < 100 || t2.Amount > 200) AddDifference(parms); } } compareLogic.Config.CustomComparers.Add( new TenantComparer(RootComparerFactory.GetRootComparer()) ); ``` -------------------------------- ### Custom Property Comparer for Specific Properties Source: https://github.com/gregfinzer/compare-net-objects/wiki/Custom-Comparers Configures custom comparison logic for a specific property within a class. This example uses a custom comparer for the 'ID' property of the 'Officer' class. ```csharp var config = new ComparisonConfig(); config.CustomPropertyComparer(officer => officer.ID, new CustomComparer((i, i1) => i % 2 == 1)); var deriveFromOfficer1 = new Officer { ID = 1, Name = "John", Type = Deck.Engineering }; var deriveFromOfficer2 = new Officer { ID = 2, Name = "John", Type = Deck.Engineering }; var derive2FromOfficer1 = new Derive2FromOfficer { Email = "a@a.com", ID = 3, Name = "John", Type = Deck.Engineering }; var derive2FromOfficer2 = new Derive2FromOfficer { Email = "a@a.com", ID = 4, Name = "John", Type = Deck.Engineering }; var compare = new CompareLogic(config); var result = compare.Compare(deriveFromOfficer1, deriveFromOfficer2); Assert.IsTrue(result.AreEqual); result = compare.Compare(derive2FromOfficer1, derive2FromOfficer2); Assert.IsTrue(result.AreEqual); ``` -------------------------------- ### Serialize Differences to JSON Source: https://github.com/gregfinzer/compare-net-objects/wiki/Output-to-JSON Use Newtonsoft.Json to serialize the Differences property of a ComparisonResult to a JSON string. Ensure Newtonsoft.Json is installed via NuGet. ```csharp Person p1 = null; Person p2 = new Person(); ComparisonResult result = _compare.Compare(p1, p2); string json = JsonConvert.SerializeObject(result.Differences, Formatting.Indented); ``` -------------------------------- ### Ignore Guid Type in Comparison Source: https://github.com/gregfinzer/compare-net-objects/wiki/Ignoring-Types Add typeof(Guid) to the TypesToIgnore list to exclude GUIDs from comparison. ```csharp CompareLogic compare = new CompareLogic(); compare.Config.TypesToIgnore.Add(typeof(Guid)); ``` -------------------------------- ### Configure CompareLogic at Construction Time Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Initialize CompareLogic with a pre-configured `ComparisonConfig` object to set various comparison parameters such as `MaxDifferences`, `CaseSensitive`, and precision settings. ```csharp ComparisonConfig config = new ComparisonConfig { MaxDifferences = 50, CaseSensitive = false, TreatStringEmptyAndNullTheSame = true, ComparePrivateProperties = true, CompareChildren = true, IgnoreCollectionOrder = false, DoublePrecision = 0.0001, DecimalPrecision = 0.01m }; CompareLogic logic = new CompareLogic(config); var result = logic.Compare(objectA, objectB); if (!result.AreEqual) Console.WriteLine(result.DifferencesString); ``` -------------------------------- ### Generate and Launch CSV Report Source: https://github.com/gregfinzer/compare-net-objects/wiki/CSV-Report This test case demonstrates generating a CSV report of differences between two objects and then launching the generated file. Ensure the 'movie.csv' file is not present before running, as it will be deleted if it exists. ```csharp using System.IO; using System; using KellermanSoftware.CompareNetObjects; public class Movie { public string Name { get; set; } public decimal PaymentForTomCruise { get; set; } } public class CsvReport { public void OutputFile(System.Collections.Generic.List differences, string filePath) { // Implementation omitted for brevity } public void LaunchApplication(string filePath) { // Implementation omitted for brevity } } public class CompareLogic { public ComparisonConfig Config { get; set; } public ComparisonResult Compare(object obj1, object obj2) { // Implementation omitted for brevity return new ComparisonResult(); } } public class ComparisonResult { public System.Collections.Generic.List Differences { get; set; } } public class Difference { // Properties omitted for brevity } public class ComparisonConfig { public int MaxDifferences { get; set; } } public class Assert { public static void IsTrue(bool condition) { // Implementation omitted for brevity } } public class Test { // Placeholder for the actual test attribute } public class CsvReportTest { [Test] public void CsvReportTest() { string expected = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "movie.csv"); if (File.Exists(expected)) File.Delete(expected); Movie movie1 = new Movie(); movie1.Name = "Oblivion"; movie1.PaymentForTomCruise = 2000000M; Movie movie2 = new Movie(); movie2.Name = "Edge of Tomorrow"; movie2.PaymentForTomCruise = 3000000M; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = Int32.MaxValue; ComparisonResult result = compareLogic.Compare(movie1, movie2); CsvReport csvReport = new CsvReport(); csvReport.OutputFile(result.Differences, expected); Assert.IsTrue(File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, expected))); csvReport.LaunchApplication(expected); } } ``` -------------------------------- ### Generate and Launch HTML Report Source: https://github.com/gregfinzer/compare-net-objects/wiki/HTML-Report This C# code demonstrates how to generate an HTML report of object differences and automatically launch it in the default browser. It includes configuration for the report title, column names, and output file path. Ensure the 'Movie' class and 'CompareLogic' are available. ```csharp using System; using System.IO; using Microsoft.VisualStudio.TestTools.UnitTesting; using KellermanSoftware.CompareNetObjects; using KellermanSoftware.CompareNetObjects.Reports; namespace CompareNetObjects.Tests { public class Movie { public string Name { get; set; } public decimal PaymentForTomCruise { get; set; } } [TestClass] public class HtmlReportTest { [TestMethod] public void HtmlReportTest() { string expected = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "movie.html"); if (File.Exists(expected)) File.Delete(expected); Movie movie1 = new Movie(); movie1.Name = "Oblivion"; movie1.PaymentForTomCruise = 2000000M; Movie movie2 = new Movie(); movie2.Name = "Edge of Tomorrow"; movie2.PaymentForTomCruise = 3000000M; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = Int32.MaxValue; ComparisonResult result = compareLogic.Compare(movie1, movie2); HtmlReport htmlReport = new HtmlReport(); // The config object is to overwrite the defaults htmlReport.Config.GenerateFullHtml = true; // if false, it will only generate an html table htmlReport.Config.HtmlTitle = "Comparison Report"; htmlReport.Config.BreadCrumbColumName = "Bread Crumb"; htmlReport.Config.ExpectedColumnName = "Expected"; htmlReport.Config.ActualColumnName = "Actual"; //htmlReport.Config.IncludeCustomCSS(".diff-crumb {background: gray;}"); // add some custom css htmlReport.OutputFile(result.Differences, expected); Assert.IsTrue(File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, expected))); htmlReport.LaunchApplication(expected); } } } ``` -------------------------------- ### Usage of Custom BaseTypeComparer Source: https://github.com/gregfinzer/compare-net-objects/wiki/Custom-Comparers Demonstrates how to register and use a custom comparer implemented via BaseTypeComparer. Shows comparison results with and without the custom comparer. ```csharp CompareLogic compareLogic = new CompareLogic(); SpecificTenant tenant1 = new SpecificTenant(); tenant1.Name = "wire"; tenant1.Amount = 37; SpecificTenant tenant2 = new SpecificTenant(); tenant2.Name = "wire"; tenant2.Amount = 155; //No Custom Comparer Assert.IsFalse(compareLogic.Compare(tenant1, tenant2).AreEqual); //specify custom selector compareLogic.Config.CustomComparers.Add(new MyCustomComparer(RootComparerFactory.GetRootComparer())); Assert.IsTrue(compareLogic.Compare(tenant1, tenant2).AreEqual); tenant2.Amount = 42; Assert.IsFalse(compareLogic.Compare(tenant1, tenant2).AreEqual); ``` -------------------------------- ### Generate User-Friendly Report of Object Differences (C#) Source: https://github.com/gregfinzer/compare-net-objects/wiki/User-Friendly-Report This C# test demonstrates how to use the UserFriendlyReport class to generate a report of differences between two objects. It saves the report to a file and also prints it to the console. Ensure the Movie class and necessary comparison logic are set up. ```csharp using System; using System.IO; using NUnit.Framework; using KellCompanies.CompareNetObjects; public class Movie { public string Name { get; set; } public decimal PaymentForTomCruise { get; set; } } [TestFixture] public class UserFriendlyReportTest { [Test] public void UserFriendlyReportTest() { string expected = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "movie.txt"); if (File.Exists(expected)) File.Delete(expected); Movie movie1 = new Movie(); movie1.Name = "Oblivion"; movie1.PaymentForTomCruise = 2000000M; Movie movie2 = new Movie(); movie2.Name = "Edge of Tomorrow"; movie2.PaymentForTomCruise = 3000000M; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = Int32.MaxValue; ComparisonResult result = compareLogic.Compare(movie1, movie2); UserFriendlyReport friendlyReport = new UserFriendlyReport(); friendlyReport.OutputFile(result.Differences, expected); Assert.IsTrue(File.Exists(expected)); Console.WriteLine(friendlyReport.OutputString(result.Differences)); friendlyReport.LaunchApplication(expected); } } ``` -------------------------------- ### Ignoring Types with ComparisonConfig Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Prevent comparison of entire value or class types by adding their `Type` objects to `TypesToIgnore` or `ClassTypesToIgnore` respectively. This is useful for types like `Guid` or custom audit information. ```csharp CompareLogic compare = new CompareLogic(); // Ignore all Guid fields/properties everywhere compare.Config.TypesToIgnore.Add(typeof(Guid)); // Ignore an entire class type during recursive comparison compare.Config.ClassTypesToIgnore.Add(typeof(AuditInfo)); var order1 = new Order { Id = Guid.NewGuid(), Amount = 100m, Audit = new AuditInfo { CreatedBy = "Admin" } }; var order2 = new Order { Id = Guid.NewGuid(), Amount = 100m, Audit = new AuditInfo { CreatedBy = "System" } }; ComparisonResult result = compare.Compare(order1, order2); Console.WriteLine(result.AreEqual); // True — Guid and AuditInfo both ignored ``` -------------------------------- ### Compare two objects in C# Source: https://github.com/gregfinzer/compare-net-objects/wiki/Getting-Started Instantiate CompareLogic, create two Person objects, compare them, and print differences if they are not equal. Ensure the Person class is defined elsewhere. ```csharp //This is the comparison class CompareLogic compareLogic = new CompareLogic(); //Create a couple objects to compare Person person1 = new Person(); person1.DateCreated = DateTime.Now; person1.Name = "Greg"; Person person2 = new Person(); person2.Name = "John"; person2.DateCreated = person1.DateCreated; ComparisonResult result = compareLogic.Compare(person1, person2); //These will be different, write out the differences if (!result.AreEqual) Console.WriteLine(result.DifferencesString); ``` -------------------------------- ### Save and Load Configuration as JSON Source: https://github.com/gregfinzer/compare-net-objects/wiki/Configuration Persist the current comparison configuration to a JSON file and load it back later. This is ideal for managing configurations across application runs or sharing them. ```csharp [Test] public void LoadConfigurationTest() { //Arrange CompareLogic compareLogic = new CompareLogic(); string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json"); compareLogic.Config.CaseSensitive = false; compareLogic.SaveConfiguration(filePath); //Act compareLogic.Config = new ComparisonConfig(); //Wipe out the current config compareLogic.LoadConfiguration(filePath); //Assert Assert.IsFalse(compareLogic.Config.CaseSensitive); } ``` -------------------------------- ### ComparisonConfig Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Allows for detailed configuration of the comparison process. A `ComparisonConfig` object can be created and populated with various settings before being passed to the `CompareLogic` constructor. ```APIDOC ## ComparisonConfig — Pass configuration at construction time A `ComparisonConfig` can be built up-front and passed directly to `CompareLogic`. ```csharp ComparisonConfig config = new ComparisonConfig { MaxDifferences = 50, CaseSensitive = false, TreatStringEmptyAndNullTheSame = true, ComparePrivateProperties = true, CompareChildren = true, IgnoreCollectionOrder = false, DoublePrecision = 0.0001, DecimalPrecision = 0.01m }; CompareLogic logic = new CompareLogic(config); var result = logic.Compare(objectA, objectB); if (!result.AreEqual) Console.WriteLine(result.DifferencesString); ``` ``` -------------------------------- ### Compare two objects in VB.NET Source: https://github.com/gregfinzer/compare-net-objects/wiki/Getting-Started Instantiate CompareLogic, create two Person objects, compare them, and print differences if they are not equal. Ensure the Person class is defined elsewhere. ```vbnet 'This is the comparison class Dim compareLogic As New CompareLogic() 'Create a couple objects to compare Dim person1 As New Person() person1.DateCreated = Date.Now person1.Name = "Greg" Dim person2 As New Person() person2.Name = "John" person2.DateCreated = person1.DateCreated Dim result As ComparisonResult = compareLogic.Compare(person1, person2) 'These will be different, write out the differences If not result.AreEqual Then Console.WriteLine(result.DifferencesString) End If ``` -------------------------------- ### Test Beyond Compare Report Generation Source: https://github.com/gregfinzer/compare-net-objects/wiki/Beyond-Compare-Report This C# test case demonstrates the process of generating comparison reports for Beyond Compare. It sets up two objects, compares them, outputs the differences to files, and optionally launches Beyond Compare if found. ```csharp using System; using System.IO; using Kellini.Compare.Objects; using Kellini.Compare.Objects.Reporters; using NUnit.Framework; public class Movie { public string Name { get; set; } public decimal PaymentForTomCruise { get; set; } } [TestFixture] public class CompareNetObjectsTests { [Test] public void BeyondCompareReportTest() { string expected = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "beyondExpected.txt"); string actual = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "beyondActual.txt"); if (File.Exists(expected)) File.Delete(expected); if (File.Exists(actual)) File.Delete(actual); Movie beyondExpected = new Movie(); beyondExpected.Name = "Oblivion"; beyondExpected.PaymentForTomCruise = 2000000M; Movie beyondActual = new Movie(); beyondActual.Name = "Edge of Tomorrow"; beyondActual.PaymentForTomCruise = 3000000M; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = Int32.MaxValue; ComparisonResult result = compareLogic.Compare(beyondExpected, beyondActual); BeyondCompareReport beyondCompare = new BeyondCompareReport(); beyondCompare.OutputFiles(result.Differences, expected, actual); Assert.IsTrue(File.Exists(expected)); Assert.IsTrue(File.Exists(actual)); if (!string.IsNullOrEmpty(beyondCompare.FindBeyondCompare())) beyondCompare.LaunchApplication(expected, actual); } } ``` -------------------------------- ### Compare Fields and Properties Source: https://github.com/gregfinzer/compare-net-objects/wiki/Class-Type-Options Set CompareFields and CompareProperties to true to include fields and properties in the comparison, respectively. The default for both is true. ```csharp /// /// If true, compare fields of a class (see also CompareProperties). The default is true. /// public bool CompareFields { get; set; } ``` ```csharp /// /// If true, compare properties of a class (see also CompareFields). The default is true. /// public bool CompareProperties { get; set; } ``` -------------------------------- ### Treat String Empty and Null the Same Source: https://github.com/gregfinzer/compare-net-objects/wiki/String-Options Configure whether `string.Empty` and `null` are considered equal. The default is false. ```csharp CompareLogic compare = new CompareLogic(); compare.Config.TreatStringEmptyAndNullTheSame = false; ``` -------------------------------- ### Capture All Differences with MaxDifferences Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Configure CompareLogic to capture all differences by setting `Config.MaxDifferences` to `int.MaxValue`. This is useful for identifying all discrepancies in an object graph. ```csharp CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = int.MaxValue; Person person1 = new Person { Name = "Greg", Age = 30 }; Person person2 = new Person { Name = "John", Age = 25 }; ComparisonResult result = compareLogic.Compare(person1, person2); Console.WriteLine($"Found {result.Differences.Count} differences"); foreach (Difference diff in result.Differences) { Console.WriteLine($" Property: {diff.PropertyName}"); Console.WriteLine($" Expected: {diff.Object1Value}"); Console.WriteLine($" Actual: {diff.Object2Value}"); } // Found 2 differences // Property: Name Expected: Greg Actual: John // Property: Age Expected: 30 Actual: 25 ``` -------------------------------- ### Compare Static Properties and Fields Source: https://github.com/gregfinzer/compare-net-objects/wiki/Class-Type-Options Set CompareStaticProperties and CompareStaticFields to true to include static properties and fields in the comparison. The default for both is true. ```csharp /// /// If true, static properties will be compared. The default is true. /// public bool CompareStaticProperties { get; set; } ``` ```csharp /// /// If true, static fields will be compared. The default is true. /// public bool CompareStaticFields { get; set; } ``` -------------------------------- ### Set Default Configuration Property Source: https://github.com/gregfinzer/compare-net-objects/wiki/Configuration Modify a specific configuration setting on the default CompareLogic instance. This is useful for quick adjustments to comparison behavior. ```csharp CompareLogic logic = new CompareLogic(); logic.Config.MaxDifferences = 100; ``` -------------------------------- ### Reacting to Differences with a Callback Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Enables registering a callback function that is invoked for every difference found during the comparison process, useful for logging or custom logic. ```APIDOC ## ComparisonConfig.DifferenceCallback — React to differences as they are found Register a callback invoked for every difference discovered during comparison, useful for logging or early-exit logic. ```csharp var differences = new List(); CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = int.MaxValue; compareLogic.Config.DifferenceCallback = diff => { differences.Add($"[{diff.PropertyName}] {diff.Object1Value} → {diff.Object2Value}"); Console.WriteLine($"Difference found: {diff}"); }; Person p1 = new Person { Name = "Greg", Age = 30 }; Person p2 = new Person { Name = "John", Age = 25 }; compareLogic.Compare(p1, p2); Console.WriteLine($"Captured {differences.Count} differences"); // Difference found: Types [Person,Person], Item Expected.Name != Actual.Name, Values (Greg,John) // Difference found: Types [Person,Person], Item Expected.Age != Actual.Age, Values (30,25) ``` ``` -------------------------------- ### Basic Deep Object Comparison with CompareLogic Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Perform a basic deep comparison between two .NET objects using CompareLogic. The result indicates if objects are equal and lists differences found up to the configured maximum. ```csharp using KellermanSoftware.CompareNetObjects; public class Person { public string Name { get; set; } public DateTime DateCreated { get; set; } public int Age { get; set; } } // Basic comparison CompareLogic compareLogic = new CompareLogic(); Person person1 = new Person { Name = "Greg", DateCreated = DateTime.Now, Age = 30 }; Person person2 = new Person { Name = "John", DateCreated = person1.DateCreated, Age = 30 }; ComparisonResult result = compareLogic.Compare(person1, person2); Console.WriteLine(result.AreEqual); // False Console.WriteLine(result.DifferencesString); // Output: // Begin Differences (1 differences): // Types [Person,Person], Item Expected.Name != Actual.Name, Values (Greg,John) // End Differences (Maximum of 1 differences shown). Console.WriteLine($"Comparison took {result.ElapsedMilliseconds}ms"); ``` -------------------------------- ### Configure Case Sensitivity Source: https://github.com/gregfinzer/compare-net-objects/wiki/Test-Extensions Set CompareExtensions.Config.CaseSensitive to false to perform case-insensitive comparisons. ```csharp CompareExtensions.Config.CaseSensitive = false; ``` -------------------------------- ### Specify Class Types to Include Source: https://github.com/gregfinzer/compare-net-objects/wiki/Class-Type-Options Use ClassTypesToInclude to define a list of class types that should be exclusively compared. If this list is populated, only these types will be considered. ```csharp /// /// Only these class types will be compared. The default is to compare all class types. /// /// If you specify a class type here no other class types will be compared unless it is in this list. public List ClassTypesToInclude { get; set; } ``` -------------------------------- ### Specify Class Types to Ignore Source: https://github.com/gregfinzer/compare-net-objects/wiki/Class-Type-Options Use ClassTypesToIgnore to provide a list of specific class types that should be excluded from the comparison. By default, all class types are compared. ```csharp /// /// A list of class types to be ignored in the comparison. The default is to compare all class types. /// public List ClassTypesToIgnore { get; set; } ``` -------------------------------- ### Enable Ignoring Collection Order and Define Matching Spec Source: https://github.com/gregfinzer/compare-net-objects/wiki/Comparing-Lists-of-Different-Lengths Set `Config.IgnoreCollectionOrder` to true to compare lists regardless of order or length. Define `Config.CollectionMatchingSpec` to specify which properties to use as keys for objects within collections, optimizing performance. ```csharp CompareLogic logic = new CompareLogic(); logic.Config.IgnoreCollectionOrder = true; var spec = new Dictionary>(); spec.Add(typeof(Customer), new string[] { "CustomerId" }); logic.Config.CollectionMatchingSpec = spec; ``` -------------------------------- ### Persist Configuration as JSON Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Serialize the current ComparisonConfig to a JSON file and reload it later. This enables shared or externalized comparison settings across different sessions or test runs. ```csharp using KellermanSoftware.CompareNetObjects; // Save CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.CaseSensitive = false; compareLogic.Config.MaxDifferences = 100; compareLogic.Config.TreatStringEmptyAndNullTheSame = true; string configPath = "compareConfig.json"; compareLogic.SaveConfiguration(configPath); // Reload in another session or test run CompareLogic anotherLogic = new CompareLogic(); anotherLogic.LoadConfiguration(configPath); Console.WriteLine(anotherLogic.Config.CaseSensitive); // False Console.WriteLine(anotherLogic.Config.MaxDifferences); // 100 Console.WriteLine(anotherLogic.Config.TreatStringEmptyAndNullTheSame); // True ``` -------------------------------- ### Specify All Types for Comparison Source: https://github.com/gregfinzer/compare-net-objects/wiki/Ignoring-Types When specifying types to ignore, only these types will be compared. You must list all types intended for comparison. ```csharp CompareLogic compare = new CompareLogic(); compare.Config.TypesToIgnore.Add(typeof(Customer)); compare.Config.TypesToIgnore.Add(typeof(String)); ``` -------------------------------- ### Ignoring Specific Members with ComparisonConfig Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Configure comparison to ignore specific members by lambda expression, class.member name, member name, wildcard, or attribute. Ensure the `CompareIgnoreAttribute` type is registered if using attributes. ```csharp // 1. Ignore by lambda expression (type-safe) CompareLogic compare = new CompareLogic(); compare.Config.IgnoreProperty(x => x.DateCreated); // 2. Ignore a specific member on a specific class compare.Config.MembersToIgnore.Add("Person.Name"); // 3. Ignore a member name on any class compare.Config.MembersToIgnore.Add("UpdateDate"); // 4. Ignore with a wildcard compare.Config.MembersToIgnore.Add("*Id"); // 5. Ignore via attribute — decorate properties with [CompareIgnore] // then register the attribute type compare.Config.AttributesToIgnore.Add(typeof(CompareIgnoreAttribute)); // 6. Compare ONLY properties that carry a specific attribute compare.Config.RequiredAttributesToCompare.Add(typeof(CompareAttribute)); Person p1 = new Person { Name = "Greg", DateCreated = DateTime.Now }; Person p2 = new Person { Name = "Greg", DateCreated = DateTime.Now.AddDays(-1) }; // DateCreated is ignored, so result is equal ComparisonResult result = compare.Compare(p1, p2); Console.WriteLine(result.AreEqual); // True ``` -------------------------------- ### Custom Comparers for Decimal Tolerance Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Add a custom comparer to handle floating-point comparisons with a tolerance. This is useful for financial calculations where exact matches are not always required. ```csharp CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.CustomComparers.Add( new CustomComparer((d1, d2) => Math.Abs(d1 - d2) < 0.01m) ); ``` -------------------------------- ### Reacting to Differences with DifferenceCallback Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Register a callback function to be invoked for every difference found during comparison. This is useful for logging differences or implementing early-exit logic. ```csharp var differences = new List(); CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = int.MaxValue; compareLogic.Config.DifferenceCallback = diff => { differences.Add($"[{diff.PropertyName}] {diff.Object1Value} → {diff.Object2Value}"); Console.WriteLine($"Difference found: {diff}"); }; Person p1 = new Person { Name = "Greg", Age = 30 }; Person p2 = new Person { Name = "John", Age = 25 }; compareLogic.Compare(p1, p2); Console.WriteLine($"Captured {differences.Count} differences"); // Difference found: Types [Person,Person], Item Expected.Name != Actual.Name, Values (Greg,John) // Difference found: Types [Person,Person], Item Expected.Age != Actual.Age, Values (30,25) ``` -------------------------------- ### Order-Insensitive Collection Comparison Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Enable order-insensitive collection comparison by setting `IgnoreCollectionOrder` to true. For performance, specify matching keys for collection item types using `CollectionMatchingSpec`. ```csharp CompareLogic logic = new CompareLogic(); logic.Config.IgnoreCollectionOrder = true; logic.Config.MaxDifferences = int.MaxValue; // Define match keys to avoid O(n²) full comparison var spec = new Dictionary>(); spec.Add(typeof(Customer), new[] { "CustomerId" }); logic.Config.CollectionMatchingSpec = spec; var list1 = new List { new Customer { CustomerId = 1, Name = "Alice" }, new Customer { CustomerId = 2, Name = "Bob" } }; var list2 = new List { new Customer { CustomerId = 2, Name = "Bob" }, new Customer { CustomerId = 1, Name = "Alice" } }; ComparisonResult result = logic.Compare(list1, list2); Console.WriteLine(result.AreEqual); // True — order ignored, matched by CustomerId ``` -------------------------------- ### Enable Comparison of Private Properties Source: https://github.com/gregfinzer/compare-net-objects/wiki/Class-Type-Options Set ComparePrivateProperties to true to include private properties in the comparison. This is useful for scenarios where private members need to be validated. ```csharp CompareLogic compare = new CompareLogic(); compare.Config.ComparePrivateProperties = true; ``` -------------------------------- ### Override Equals with CompareLogic in C# Source: https://github.com/gregfinzer/compare-net-objects/wiki/Overriding-Equals Implement the Equals method in a base class using CompareLogic. Ensure `_Config.UseHashCodeIdentifier` is set to true to avoid potential stack overflow exceptions. ```csharp public class MyLovelyBaseClass { public override bool Equals(object? obj) { if (obj == null || this.GetType() != obj.GetType()) return false; MyLovelyBaseClass demo = (MyLovelyBaseClass)obj; return Compare(demo, this); } public override int GetHashCode() { return base.GetHashCode(); } private bool Compare(object object1, object object2) { CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.UseHashCodeIdentifier = true; ComparisonResult result = compareLogic.Compare(object1, object2); return result.AreEqual; } } ``` -------------------------------- ### Generate HTML Report of Differences Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Create a styled HTML table detailing all differences between two objects using the HtmlReport class. This report can include a full HTML document wrapper and custom CSS. ```csharp using KellermanSoftware.CompareNetObjects; using KellermanSoftware.CompareNetObjects.Reports; Movie movie1 = new Movie { Name = "Oblivion", PaymentForTomCruise = 2_000_000M }; Movie movie2 = new Movie { Name = "Edge of Tomorrow", PaymentForTomCruise = 3_000_000M }; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = int.MaxValue; ComparisonResult result = compareLogic.Compare(movie1, movie2); HtmlReport htmlReport = new HtmlReport(); htmlReport.Config.GenerateFullHtml = true; htmlReport.Config.HtmlTitle = "Movie Comparison Report"; htmlReport.Config.BreadCrumbColumName = "Property"; htmlReport.Config.ExpectedColumnName = "Expected"; htmlReport.Config.ActualColumnName = "Actual"; string filePath = "comparison.html"; htmlReport.OutputFile(result.Differences, filePath); // Or output to a string (e.g., for embedding in an API response) string html = htmlReport.OutputString(result.Differences); // Launch in default browser (Windows only) // htmlReport.LaunchApplication(filePath); ``` -------------------------------- ### Order-Insensitive Collection Comparison Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Enables comparison of collections where the order of items does not matter, with an option to specify key properties for performance. ```APIDOC ## ComparisonConfig.IgnoreCollectionOrder / CollectionMatchingSpec — Order-insensitive collection comparison Compare collections where items may be in different orders, with an optional key spec to improve performance. ```csharp CompareLogic logic = new CompareLogic(); logic.Config.IgnoreCollectionOrder = true; logic.Config.MaxDifferences = int.MaxValue; // Define match keys to avoid O(n²) full comparison var spec = new Dictionary>(); spec.Add(typeof(Customer), new[] { "CustomerId" }); logic.Config.CollectionMatchingSpec = spec; var list1 = new List { new Customer { CustomerId = 1, Name = "Alice" }, new Customer { CustomerId = 2, Name = "Bob" } }; var list2 = new List { new Customer { CustomerId = 2, Name = "Bob" }, new Customer { CustomerId = 1, Name = "Alice" } }; ComparisonResult result = logic.Compare(list1, list2); Console.WriteLine(result.AreEqual); // True — order ignored, matched by CustomerId ``` ``` -------------------------------- ### CompareLogic.Compare Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Performs a deep, reflection-based comparison of two .NET objects. It returns a ComparisonResult object detailing any differences found, up to the limit specified by Config.MaxDifferences. ```APIDOC ## CompareLogic.Compare — Basic deep object comparison The primary method of the library. Accepts two objects of any type and returns a `ComparisonResult` describing every difference found up to `Config.MaxDifferences` (default 1 for performance). ```csharp using KellermanSoftware.CompareNetObjects; public class Person { public string Name { get; set; } public DateTime DateCreated { get; set; } public int Age { get; set; } } // Basic comparison CompareLogic compareLogic = new CompareLogic(); Person person1 = new Person { Name = "Greg", DateCreated = DateTime.Now, Age = 30 }; Person person2 = new Person { Name = "John", DateCreated = person1.DateCreated, Age = 30 }; ComparisonResult result = compareLogic.Compare(person1, person2); Console.WriteLine(result.AreEqual); // False Console.WriteLine(result.DifferencesString); // Output: // Begin Differences (1 differences): // Types [Person,Person], Item Expected.Name != Actual.Name, Values (Greg,John) // End Differences (Maximum of 1 differences shown). Console.WriteLine($"Comparison took {result.ElapsedMilliseconds}ms"); ``` ``` -------------------------------- ### Implement BaseTypeComparer for Custom Comparison Source: https://github.com/gregfinzer/compare-net-objects/wiki/Custom-Comparers Implement BaseTypeComparer to define custom comparison logic for specific types. Custom comparers are executed before built-in ones. ```csharp public class MyCustomComparer : BaseTypeComparer { public MyCustomComparer(RootComparer rootComparer) : base(rootComparer) { } public override bool IsTypeMatch(Type type1, Type type2) { return type1 == typeof (SpecificTenant); } public override void CompareType(CompareParms parms) { var st1 = (SpecificTenant)parms.Object1; var st2 = (SpecificTenant)parms.Object2; if (st1.Name != st2.Name || st1.Amount > 100 || st2.Amount < 100) { AddDifference(parms); } } } ``` -------------------------------- ### Per-Property Custom Comparer for Salary Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Define a custom comparer for a specific property (Salary) of a class (Officer) using a lambda expression. This allows fine-grained control over property comparisons. ```csharp var config = new ComparisonConfig(); config.CustomPropertyComparer( o => o.Salary, new CustomComparer((s1, s2) => Math.Abs(s1 - s2) < 1000m) ); var logic = new CompareLogic(config); var o1 = new Officer { Name = "Kirk", Salary = 50000m }; var o2 = new Officer { Name = "Kirk", Salary = 50500m }; Console.WriteLine(logic.Compare(o1, o2).AreEqual); // True — within $1000 tolerance ``` -------------------------------- ### Compare Private Properties and Fields Source: https://github.com/gregfinzer/compare-net-objects/wiki/Class-Type-Options Set ComparePrivateProperties and ComparePrivateFields to true to include private properties and fields in the comparison. Note that Silverlight and WinRT may restrict access to private variables. ```csharp /// /// If true, private properties and fields will be compared. The default is false. Silverlight and WinRT restricts access to private variables. /// public bool ComparePrivateProperties { get; set; } ``` ```csharp /// /// If true, private fields will be compared. The default is false. Silverlight and WinRT restricts access to private variables. /// public bool ComparePrivateFields { get; set; } ``` -------------------------------- ### ComparisonConfig.MaxDifferences Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Configures the maximum number of differences to capture during comparison. By default, it's set to 1 for performance. Setting it to `int.MaxValue` allows capturing all differences within the object graph. ```APIDOC ## ComparisonConfig.MaxDifferences — Capture all differences instead of stopping at first By default `MaxDifferences` is 1. Set it to `int.MaxValue` to find every difference in the object graph. ```csharp CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = int.MaxValue; Person person1 = new Person { Name = "Greg", Age = 30 }; Person person2 = new Person { Name = "John", Age = 25 }; ComparisonResult result = compareLogic.Compare(person1, person2); Console.WriteLine($"Found {result.Differences.Count} differences"); foreach (Difference diff in result.Differences) { Console.WriteLine($" Property: {diff.PropertyName}"); Console.WriteLine($" Expected: {diff.Object1Value}"); Console.WriteLine($" Actual: {diff.Object2Value}"); } // Found 2 differences // Property: Name Expected: Greg Actual: John // Property: Age Expected: 30 Actual: 25 ``` ``` -------------------------------- ### Comparing Objects of Different Types Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Allows comparison between objects of different concrete types, useful for comparing DTOs with domain objects, by ignoring type differences. ```APIDOC ## ComparisonConfig.IgnoreObjectTypes — Comparing objects of different types Allow comparison across class hierarchies by ignoring concrete type differences, useful when comparing DTOs to domain objects. ```csharp public class RecipeDetail { public string Ingredient { get; set; } } public class IndianRecipeDetail { public string Ingredient { get; set; } } RecipeDetail detail1 = new RecipeDetail { Ingredient = "Crunchy Chocolate" }; IndianRecipeDetail detail2 = new IndianRecipeDetail { Ingredient = "Crunchy Chocolate" }; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.IgnoreObjectTypes = true; ComparisonResult result = compareLogic.Compare(detail1, detail2); Console.WriteLine(result.AreEqual); // True ``` ``` -------------------------------- ### Implement Custom Comparer with a Function Source: https://github.com/gregfinzer/compare-net-objects/wiki/Custom-Comparers Utilizes the generic CustomComparer with a lambda expression for concise custom comparison logic. Allows for dynamic rule changes. ```csharp CompareLogic compareLogic = new CompareLogic(); SpecificTenant tenant1 = new SpecificTenant(); tenant1.Name = "wire"; tenant1.Amount = 37; SpecificTenant tenant2 = new SpecificTenant(); tenant2.Name = "wire"; tenant2.Amount = 155; //No Custom Comparer Assert.IsFalse(compareLogic.Compare(tenant1, tenant2).AreEqual); //specify custom selector //using the same rule as MyCustomComparer compareLogic.Config.CustomComparers.Add(new CustomComparer( (st1, st2) => !(st1.Name != st2.Name || st1.Amount > 100 || st2.Amount < 100) )); Assert.IsTrue(compareLogic.Compare(tenant1, tenant2).AreEqual); tenant2.Amount = 42; Assert.IsFalse(compareLogic.Compare(tenant1, tenant2).AreEqual); //Change rule at runtime var comparerer = compareLogic.Config.CustomComparers[0] as CustomComparer; comparerer.Compare = (st1, st2) => !(st1.Name != st2.Name || st1.Amount > 100 || st2.Amount < 40); Assert.IsTrue(compareLogic.Compare(tenant1, tenant2).AreEqual); ``` -------------------------------- ### Output Differences as JSON Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Serialize the Differences list to a JSON string using Newtonsoft.Json. This is useful for auditing or further processing of comparison results. ```csharp using KellermanSoftware.CompareNetObjects; using Newtonsoft.Json; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = int.MaxValue; Person p1 = null; Person p2 = new Person { Name = "Alice", Age = 30 }; ComparisonResult result = compareLogic.Compare(p1, p2); string json = JsonConvert.SerializeObject(result.Differences, Formatting.Indented); Console.WriteLine(json); // [ // { // "PropertyName": "", // "Object1Value": "null", // "Object2Value": "KellermanSoftware.CompareNetObjects.Tests.TestClasses.Person", // ... // } // ] ``` -------------------------------- ### Ignore Unknown Object Types Source: https://github.com/gregfinzer/compare-net-objects/wiki/Class-Type-Options Set IgnoreUnknownObjectTypes to true to prevent exceptions when encountering unknown object types during comparison. The default is false. ```csharp /// /// If true, unknown object types will be ignored instead of throwing an exception. The default is false. /// public bool IgnoreUnknownObjectTypes { get; set; } ``` -------------------------------- ### Generate CSV Report of Differences Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Export a list of all differences between two objects to a CSV file. The CSV includes 'Bread Crumb', 'Expected', and 'Actual' columns, suitable for programmatic processing or opening in spreadsheet software. ```csharp using KellermanSoftware.CompareNetObjects; using KellermanSoftware.CompareNetObjects.Reports; CompareLogic compareLogic = new CompareLogic(); compareLogic.Config.MaxDifferences = int.MaxValue; ComparisonResult result = compareLogic.Compare(objectA, objectB); CsvReport csvReport = new CsvReport(); // Write to file csvReport.OutputFile(result.Differences, "differences.csv"); // Or capture as string string csv = csvReport.OutputString(result.Differences); Console.WriteLine(csv); // Bread Crumb,Expected,Actual // Name,Oblivion,Edge of Tomorrow // PaymentForTomCruise,2000000,3000000 ``` -------------------------------- ### Ignoring Specific Members Source: https://context7.com/gregfinzer/compare-net-objects/llms.txt Demonstrates multiple ways to exclude members from comparison, including lambda expressions, specific member names, wildcards, and attributes. ```APIDOC ## ComparisonConfig.MembersToIgnore / IgnoreProperty — Ignoring specific members Members can be excluded by name, by `ClassName.MemberName`, by wildcard, by attribute, or via a strongly-typed lambda expression. ```csharp // 1. Ignore by lambda expression (type-safe) CompareLogic compare = new CompareLogic(); compare.Config.IgnoreProperty(x => x.DateCreated); // 2. Ignore a specific member on a specific class compare.Config.MembersToIgnore.Add("Person.Name"); // 3. Ignore a member name on any class compare.Config.MembersToIgnore.Add("UpdateDate"); // 4. Ignore with a wildcard compare.Config.MembersToIgnore.Add("*Id"); // 5. Ignore via attribute — decorate properties with [CompareIgnore] // then register the attribute type compare.Config.AttributesToIgnore.Add(typeof(CompareIgnoreAttribute)); // 6. Compare ONLY properties that carry a specific attribute compare.Config.RequiredAttributesToCompare.Add(typeof(CompareAttribute)); Person p1 = new Person { Name = "Greg", DateCreated = DateTime.Now }; Person p2 = new Person { Name = "Greg", DateCreated = DateTime.Now.AddDays(-1) }; // DateCreated is ignored, so result is equal ComparisonResult result = compare.Compare(p1, p2); Console.WriteLine(result.AreEqual); // True ``` ```