Как создать DataTable, который содержит результат сравнения двух DataTable, имеющих одинаковую структуру? - PullRequest
2 голосов
/ 04 октября 2019

Этот вопрос уже задавался несколько раз на Переполнение стека , но прежде чем пометить его как дубликат, пожалуйста, посмотрите на мою проблему.

Я пытаюсь сравнить две таблицы базы данных для измененных значений.
По сути, я пытаюсь регистрировать обновленные данные путем сравнения.

Это мои таблицы базы данных, которые необходимо сравнить дляизменения:

My DataTables

В таблице 2 Id 3 и 4 получили обновленные значения. Мне нужно получить эти значения и сохранить их в третьей таблице базы данных. Будет намного лучше, если я получу некоторый универсальный класс для этого.

Кроме того, если кто-нибудь добавит еще немного и скажет мне, как я буду сравнивать изменения в случае списка 2 одного типа и возвращать списокэтого типа только с измененными значениями?

Здесь я прошу больше, но может ли кто-нибудь мне помочь.

Ответы [ 2 ]

0 голосов
/ 05 октября 2019

Comparer

.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 реализация тупая и довольно бесполезная, но она работает;). Прочитайте эту ветку , чтобы узнать, как правильно ее реализовать. Прочитайте этот поток , чтобы узнать, где он используется, и если это так важно для его реализации в вашем случае.

2) Я проверяю значения для null, потому что я ненавижу NullReferenceException с. Однако это может быть не так, потому что нулевые значения в 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);
}
0 голосов
/ 04 октября 2019

Чтобы создать DataTable, который содержит результат сравнения двух DataTable, имеющих одинаковую структуру, вы можете попробовать это и адаптировать его для предполагаемого использования:

    const string DiffColumnName = "Column Name";
    const string DiffTable1Value = "Value From Table 1";
    const string DiffTable2Value = "Value From Table 2";

    static private DataTable GetDifferentRecords(DataTable dataTable1,
                                                 DataTable dataTable2,
                                                 string keyIDName,
                                                 string tableDiffName)
    {
      var result = new DataTable(tableDiffName);

      if ( dataTable1.Columns.Count != dataTable2.Columns.Count )
        throw new Exception("Tables have not the same columns count.");

      for ( int indexColumn = 0; indexColumn < dataTable1.Columns.Count; indexColumn++ )
      {
        string columnName1 = dataTable1.Columns[indexColumn].ColumnName;
        string columnName2 = dataTable2.Columns[indexColumn].ColumnName;
        if ( columnName1 != columnName2 )
          throw new Exception($"Tables columns name mismatch : {columnName1} <=> {columnName2}");
        var columnType1 = dataTable1.Columns[indexColumn].DataType;
        var columnType2 = dataTable2.Columns[indexColumn].DataType;
        if ( columnType1 != columnType2 )
          throw new Exception($"Tables columns type mismatch : {columnType1.Name} <=> {columnType2.Name}");
        if ( dataTable1.Columns[indexColumn].ColumnName == keyIDName )
        {
          result.Columns.Add(keyIDName, columnType1);
          result.Columns.Add(DiffColumnName, typeof(string));
          result.Columns.Add(DiffTable1Value, typeof(object));
          result.Columns.Add(DiffTable2Value, typeof(object));
        }
      }

      for ( int indexRow = 0; indexRow < dataTable1.Rows.Count; indexRow++ )
      {
        object columnKeyValue = null;
        for ( int indexColumn = 0; indexColumn < dataTable1.Columns.Count; indexColumn++ )
          if ( dataTable1.Columns[indexColumn].ColumnName == keyIDName )
            columnKeyValue = dataTable1.Rows[indexRow][indexColumn].ToString();
        if ( columnKeyValue == null )
          throw new Exception($"Key not found : {keyIDName}");
        for ( int indexColumn = 0; indexColumn < dataTable1.Columns.Count; indexColumn++ )
        {
          string columnName1 = dataTable1.Columns[indexColumn].ColumnName;
          string columnName2 = dataTable2.Columns[indexColumn].ColumnName;
          var value1 = dataTable1.Rows[indexRow][indexColumn];
          var value2 = dataTable2.Rows[indexRow][indexColumn];
          if ( !value1.Equals(value2) )
          {
            var row = result.NewRow();
            row[keyIDName] = columnKeyValue;
            row[DiffColumnName] = columnName1;
            row[DiffTable1Value] = value1;
            row[DiffTable2Value] = value2;
            result.Rows.Add(row);
          }
        }
      }

      return result;
    }

Тест:

static private void DataTableDiffTest()
{
  DataTable before = new DataTable("InventoryItem");
  before.Columns.Add("Id", typeof(int));
  before.Columns.Add("Name", typeof(string));
  before.Columns.Add("Age", typeof(decimal));
  before.Rows.Add(1, "Tom", 24);
  before.Rows.Add(2, "Dick", 23);
  before.Rows.Add(3, "Broad", 35);
  before.Rows.Add(4, "Anderson", 29);

  DataTable after = new DataTable("InventoryItems");
  after.Columns.Add("Id", typeof(int));
  after.Columns.Add("Name", typeof(string));
  after.Columns.Add("Age", typeof(decimal));
  after.Rows.Add(1, "Tom", 24);
  after.Rows.Add(2, "Dick", 23);
  after.Rows.Add(3, "Broad2", 31);
  after.Rows.Add(4, "Anderson", 30);

  try
  {
    var diffDataTable = GetDifferentRecords(before, after, "Id", "TableDiff");

    var diffTree = new Dictionary<object,
                                  Dictionary<string,
                                             Tuple<object, object>>>();

    foreach ( DataRow item in diffDataTable.Rows )
    {
      var items = new Tuple<object, object>(item[DiffTable1Value], item[DiffTable2Value]);
      if ( diffTree.ContainsKey(item["Id"]) )
        diffTree[item["Id"]].Add(item[DiffColumnName].ToString(), items);
      else
      {
        var mismatch = new Dictionary<string, Tuple<object, object>>();
        mismatch.Add(item[DiffColumnName].ToString(), items);
        diffTree.Add(item["Id"], mismatch);
      }
    }

    foreach ( var item in diffTree )
    {
      Console.WriteLine($"Id({item.Key}) mismatch:");
      foreach ( var value in item.Value )
      {
        Console.Write($"  Column({value.Key.ToString()}): ");
        Console.WriteLine($"{ value.Value.Item1.ToString()} <=> { value.Value.Item2.ToString()} ");
      }
      Console.WriteLine();
    }

  }
  catch ( Exception ex )
  {
    Console.WriteLine(ex.Message);
  }
}

Выход:

Id(3) mismatch:
  Column(Name): Broad <=> Broad2
  Column(Age): 35 <=> 31

Id(4) mismatch:
  Column(Age): 29 <=> 30
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...