Comparer
.net BCL contains abstractions that you can use to declare custom comparison that fits your needs:
class RowDataComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
// add check for nulls, different ItemArray length, whatever
var xv = x.ItemArray;
var yv = y.ItemArray;
for (int i = 0; i < xv.Length; i++)
if(xv[i] == null && yv[i] == null)
continue;
else if (xv[i]==null || !xv[i].Equals(yv[i]))
return false;
return true;
}
public int GetHashCode(DataRow obj)
=> obj[0]?.GetHashCode() ?? 0;
}
Few interesting things in this comparer you need to know about:
1) GetHashCode
implementation is dumb and rather useless, but it works ;). Read this thread to know how to implement it correctly. Read this thread to know where it is used and if it is so critical to implement it in your case.
2) I do check values for null
, because I hate NullReferenceException
s. However, that might not be the case here, because null values in DataRow
is replaced by DBNull.Value
3) I don't use operator ==
to compare objects. It might work fine for reference types which implement Equals/GetHashCode
, otherwise it will compare references. Boxed values always have different references, so if you have for example integer columns, ==
will not work. Try this to feel the difference: Console.WriteLine((object)42 == (object)42)
and Console.WriteLine(((object)42).Equals((object)42))
Diffs
Assuming that schema is identical, keys are integers and you don't care about extra/missing rows you can use something like below:
class RowsComparer
{
Dictionary<int, DataRow> _leftRows;
Dictionary<int, DataRow> _rightRows;
HashSet<int> _commonKeys;
private static Dictionary<int, DataRow> toRows(DataTable table)
=> table.Rows.OfType<DataRow>().ToDictionary(r => (int)r.ItemArray[0]);
public RowsComparer(DataTable left, DataTable right)
{
_leftRows = toRows(left);
_rightRows = toRows(right);
_commonKeys = new HashSet<int>(_leftRows.Keys.Intersect(_rightRows.Keys));
}
public IEnumerable<DataRow> Diffs()
=> _commonKeys.Select(k => _rightRows[k]).Except(_leftRows.Values, new RowDataComparer());
public IEnumerable<DataRow> Extra()
=> _leftRows.Where(kv => !_commonKeys.Contains(kv.Key)).Select(kv => kv.Value);
public IEnumerable<DataRow> Missing()
=> _rightRows.Where(kv => !_commonKeys.Contains(kv.Key)).Select(kv => kv.Value);
}