【问题标题】:How to create a DataTable that contains the comparaison result of two DataTables having the same structure?如何创建包含两个具有相同结构的数据表的比较结果的数据表?
【发布时间】:2019-10-04 17:05:05
【问题描述】:

这个问题已经在 Stack Overflow 上被问过好几次了,但是在将其标记为重复之前,请先看看我的问题。

我正在尝试比较两个数据库表的更改值。
基本上,我试图通过比较来记录更新的数据。

这些是我需要比较更改的数据库表:

在表 2 中,Id 3 和 4 得到了更新的值。我需要获取这些值并将它们存储在第三个数据库表中。 如果我为此获得一些通用类会更好。

另外,如果有人添加更多内容并告诉我如何比较相同类型的 2 个列表的更改并仅返回具有更改值的该类型列表?

在这里,我要求更多,但如果有人可以帮助我。

【问题讨论】:

  • 感谢您抽出宝贵时间分享您的问题。但是您的问题中缺少一些东西。你的目标是什么?你的困难是什么?到目前为止你做了什么?请尝试更好地解释您的问题、您的开发环境并分享更多代码或屏幕截图。为了帮助您改进查询的内容、标题和标签,请考虑阅读帮助中心的 How do I ask a good question
  • 这不是How to compare 2 dataTables的可能重复项吗?
  • 我试过了,但它返回了我没有看到的不需要的行。我只关心那些被改变的行。如果我正在创建一个用于查找差异的函数,那么该函数应返回仅包含更改值的数据表。
  • 您能否添加一些关于您想要控制的实例的代码,例如数据表声明和对它们的查询?

标签: c# .net datatable data-comparison


【解决方案1】:

比较器

.net BCL 包含可用于声明满足您需求的自定义比较的抽象:

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;
}

这个比较器中你需要知道的一些有趣的事情:

1) GetHashCode 的实现是愚蠢的而且相当无用,但它确实有效;)。阅读this thread 了解如何正确实现它。阅读this thread,了解它的用途以及在您的情况下实施它是否如此重要。

2) 我确实检查了null 的值,因为我讨厌NullReferenceExceptions。但是,这里可能不是这样,因为DataRow 中的空值被DBNull.Value 替换

3) 我不使用运算符== 来比较对象。它可能适用于实现Equals/GetHashCode 的引用类型,否则它将比较引用。装箱的值总是有不同的引用,所以如果你有例如整数列,== 将不起作用。试试这个来感受不同:Console.WriteLine((object)42 == (object)42)Console.WriteLine(((object)42).Equals((object)42))

差异

假设架构是相同的,键是整数,并且您不关心额外/缺失的行,您可以使用如下所示:

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);
}

【讨论】:

  • 编译器错误 CS0266 *Impossible de convertir implicitement le type 'bool?' zh '布尔'。 Une conversion explicite exists (un cast est-il manquant ?)"
  • @Oliver,我更新了答案。我真的希望您会发现它很有用,而不仅仅是盲目地复制粘贴提供的代码;)如果您发现错误,您也可以提出修改建议。无论如何,谢谢你的注意:)
  • 不确定我是否遵循您的问题,但我想答案将是:类似于其他代码 - 带有单元测试。设计是相当可测试的,因为关注点是分开的,它并不理想,可能我会拆分比较器(或提取方法)以仅匹配对象数组并且不使用凌乱的 DataRows。此外,我认为数据驱动测试非常适合这里。
  • 我使用var res = new RowsComparer(before, after); foreach ( var v in res.Diffs() ) Console.WriteLine(v["Id"]);,但我得到了1 2 3 4。所以看来你的解决方案不起作用。
  • 这实际上是一个很好的收获!我在潜在问题的初始答案中添加了一个链接 :) 请参阅 GetHashCode() 实现。
猜你喜欢
  • 1970-01-01
  • 2014-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-29
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多