2

This question has already been asked several times on Stack Overflow, but before marking it as a duplicate, please have a look at my issue.

I am trying to compare two database tables for changed values.
Basically, I am trying to log updated data by comparing.

These are my database tables which need to be compared for changes:

My DataTables

In Table 2, Id 3 and 4 have got updated values. I need to get these values and store them in a 3rd database table. It will be much better if I get some generic class for this.

Also, if anybody add some more and tell me how do I will compare for a change in case of 2 list of same type and return the list of that type only with changed values?

Here, I am asking for more but if somebody can help me out.

Abra
  • 19,142
  • 7
  • 29
  • 41
Glenn singh
  • 233
  • 1
  • 6
  • 18
  • Thank you for taking the time to share your problem. But there is something missing from your question. What is your goal? What is your difficulty? What have you done so far? Please try to better explain your issue, your dev environment and share more code or some screenshot of your screeen. To help you improve the content, title and tags of your query, consider reading the *[How do I ask a good question](https://stackoverflow.com/help/how-to-ask)* which is in the help center. –  Oct 04 '19 at 17:06
  • Is it not a possible duplicate of [How to compare 2 dataTables](https://stackoverflow.com/questions/7517968/how-to-compare-2-datatables) ? –  Oct 04 '19 at 17:09
  • I tried this but it's returning unwanted rows which I am not looking. I am only bothered of those rows which were changed. If I am creating a function for finding the diff then that function should return datatable containing only the changed values. – Glenn singh Oct 04 '19 at 17:34
  • Can you add some code about the instances you want to control like datatables declarations and queries on them, please? –  Oct 04 '19 at 17:36
  • I have created a gist. Please check this out https://gist.github.com/Glennsingh44/251e88701dd28b46b87717fa58cf4fb0 – Glenn singh Oct 04 '19 at 17:46
  • My goal here is to create a generic class which can solve my problem of finding difference by using datatable or list. I found examples with datatable finding the differences but nothing is working for me. – Glenn singh Oct 04 '19 at 17:50
  • You want to get a third datatable containing only a duplication of records found in dt1 and dt2 that have same id but where there is at least one different field? What do you call a `generic` class here? Does a method enough? –  Oct 04 '19 at 18:02
  • I begin to understand your goal, I think. But you can't create a data table to add rows that mismatch on values because of the ID that will be duplicated. Isn't it? –  Oct 04 '19 at 18:21
  • Yes you are correct. Let's forget about generic class. How can i achieve this with function. – Glenn singh Oct 04 '19 at 18:25
  • @Glenn singh, please check solution in my answer if it fits your needs. Thanks. – fenixil Oct 06 '19 at 05:46
  • @fenixil Sure I will try your answer as well. Thanks for that.But for the time being what Olivier Rogier has given, works for me. Thanks ! – Glenn singh Oct 23 '19 at 15:53

1 Answers1

-1

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 NullReferenceExceptions. 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);
}
fenixil
  • 2,106
  • 7
  • 13
  • Compiler Error CS0266 *Impossible de convertir implicitement le type 'bool?' en 'bool'. Une conversion explicite existe (un cast est-il manquant ?)" –  Oct 05 '19 at 04:49
  • @Oliver, I updated the answer. I do really hope that you'll find it useful rather than just blindly copy-paste provided code ;) if you spot an error, you can also propose an edit. Anyway thanks for a note :) – fenixil Oct 05 '19 at 04:58
  • Not sure I follow your question, but I guess the answer will be: similar to other code - with unit-tests. Design is rather testable because concerns are separated, it is not ideal, probably I'd split comparers (or extract metho) to be able to match object arrays only and don't work with messy DataRows. Also I think data driven testing will fit here very well. – fenixil Oct 05 '19 at 05:20
  • 1
    I use `var res = new RowsComparer(before, after); foreach ( var v in res.Diffs() ) Console.WriteLine(v["Id"]);`but I got `1 2 3 4`. So it seems your solution does not work. –  Oct 05 '19 at 05:27
  • That's actually a good catch! I added a link in the initial answer to the potential issue :) See `GetHashCode()` implementation. – fenixil Oct 05 '19 at 05:37