public static bool DiffWith(this DataTable first, DataTable second, IEnumerable <string> primaryKeyColumns, IEnumerable <string> valueColumns, out DataTable matches) { matches = new DataTable(); matches.Columns.Add("PkeyDelta", typeof(int)); matches.Columns.Add("ValueDelta", typeof(int)); matches.Columns.Add("OldRow", typeof(DataRow)); matches.Columns.Add("NewRow", typeof(DataRow)); bool ret = false; var firstRows = first.GetRows(); var secondRows = second.GetRows(); var f = firstRows.ToList(); var s = secondRows.ToList(); string[] pkeyColumnNames = primaryKeyColumns.ToArray(); string[] valueColumnNames = valueColumns.ToArray(); var secondIndex = new TableIndex(second); Dictionary <DataRow, object> assertDictionary1 = first.GetRows().ToDictionaryDX(NULL); //these are to make sure no records are missing/lost/double removed/etc. Dictionary <DataRow, object> assertDictionary2 = second.GetRows().ToDictionaryDX(NULL); for (int pkeyDelta = 0; pkeyDelta < primaryKeyColumns.Count(); pkeyDelta++) { for (int valueDelta = 0; valueDelta < valueColumns.Count(); valueDelta++) { List <int> removals = new List <int>(); for (int i = 0; i < f.Count; i++) { var frow = f[i]; DataRow found = FindFuzzyMatch(frow, pkeyDelta, valueDelta, pkeyColumnNames, valueColumnNames, secondIndex); if (found != null) { matches.Rows.Add(pkeyDelta, valueDelta, frow, found); assertDictionary2.Remove(found); secondIndex.Remove(found); removals.Add(i); } } for (int i = removals.Count - 1; i >= 0; i--) { assertDictionary1.Remove(f[removals[i]]); f.RemoveAt(removals[i]); } if (pkeyDelta == 0 && valueDelta == 0 && assertDictionary1.Count == 0 && assertDictionary2.Count == 0) { ret = true; } } } for (int i = 0; i < f.Count; i++) { matches.Rows.Add(-1, -1, f[i], null); assertDictionary1.Remove(f[i]); } for (int i = 0; i < s.Count; i++) { matches.Rows.Add(-1, -1, null, s[i]); assertDictionary2.Remove(s[i]); } Debug.Assert(assertDictionary1.Count == 0); Debug.Assert(assertDictionary2.Count == 0); return(ret); }