Это ошибка в DataTable API?Изменения сохраняются / выполняются в “неправильной последовательности”

StackOverflow https://stackoverflow.com/questions/1027095

  •  06-07-2019
  •  | 
  •  

Вопрос

Редактировать:будем признательны за любые комментарии, независимо от того, считаете ли вы, что это ошибка .NET или нет.

У меня есть ошибка, которую мне удалось упростить до следующего сценария:

У меня есть DataTable, где первичные ключи должны храниться последовательно, напримересли вы вставляете строку между другими строками, вы должны сначала увеличить идентификатор последующих строк, чтобы освободить место, а затем вставить строку.

И если вы удаляете строку, вы должны уменьшить идентификатор всех последующих строк, чтобы заполнить пробел, оставленный строкой в таблице.

Тестовый пример, который работает правильно

Начните с 3 строк в таблице с идентификаторами 1, 2 и 3.

Затем удалите ID = 2 и установите ID = 2, где ID = 3 (чтобы заполнить пробел);это работает правильно.Таблица данных.GetChanges() содержит удаленную строку, а затем измененную строку;когда вы запускаете DataAdapter.Обновление (таблицы) выполняется нормально.

Тестовый пример, который не работает

Однако, если вы начинаете с 2 строк (идентификаторы 1 и 2), затем устанавливаете ID = 3, где ID= 2, и вставляете ID = 2, затем фиксируете (или принимаете) изменения.Теперь это должно быть то же состояние, что и в первом тесте.

Затем вы выполняете те же шаги, что и раньше, т.е.удалите ID = 2 и установите ID = 2, где ID = 3, но теперь DataTable.GetChanges() расположены в неправильном порядке.Первая строка - это измененная строка, а вторая строка - удаленная строка.Затем, если вы попробуете DataAdapter.Обновить (таблицу), это приведет к нарушению первичного ключа - он пытался изменить строку на уже существующую строку перед удалением.

Обходной путь

Я могу придумать обходной путь к этой проблеме, т.е.принудительно выполните это так, чтобы сначала были зафиксированы удаленные строки, затем измененные строки, а затем добавленные строки.Но почему это происходит?Есть ли другое решение?

Я думаю, что раньше я видел подобную "проблему" со словарями, что если вы добавляете некоторые элементы, затем удаляете, повторно вставляете их, то они не будут находиться в той же последовательности, в которой вы их добавили (при перечислении словаря).

Вот два теста NUnit, которые показывают проблему:

[Test]
public void GetChanges_Working()
{
    // Setup ID table with three rows, ID=1, ID=2, ID=3
    DataTable idTable = new DataTable();
    idTable.Columns.Add("ID", typeof(int));

    idTable.PrimaryKey = new DataColumn[] { idTable.Columns["ID"] };

    idTable.Rows.Add(1);
    idTable.Rows.Add(2);
    idTable.Rows.Add(3);

    idTable.AcceptChanges();

    // Delete ID=2, and move old ID=3 to ID=2
    idTable.Select("ID = 2")[0].Delete();
    idTable.Select("ID = 3")[0]["ID"] = 2;

    // Debug GetChanges
    foreach (DataRow row in idTable.GetChanges().Rows)
    {
        if (row.RowState == DataRowState.Deleted)
            Console.WriteLine("Deleted: {0}", row["ID", DataRowVersion.Original]);
        else
            Console.WriteLine("Modified: {0} = {1}", row["ID", DataRowVersion.Original], row["ID", DataRowVersion.Current]);
    }

    // Check GetChanges
    Assert.AreEqual(DataRowState.Deleted, idTable.GetChanges().Rows[0].RowState, "1st row in GetChanges should be deleted row");
    Assert.AreEqual(DataRowState.Modified, idTable.GetChanges().Rows[1].RowState, "2nd row in GetChanges should be modified row");
}

Выходной сигнал:

Deleted: 2
Modified: 3 = 2

1 passed, 0 failed, 0 skipped, took 4.27 seconds (NUnit 2.4).

Следующий тест:

[Test]
public void GetChanges_NotWorking()
{
    // Setup ID table with two rows, ID=1, ID=2
    DataTable idTable = new DataTable();
    idTable.Columns.Add("ID", typeof(int));

    idTable.PrimaryKey = new DataColumn[] { idTable.Columns["ID"] };

    idTable.Rows.Add(1);
    idTable.Rows.Add(2);

    idTable.AcceptChanges();

    // Move old ID=2 to ID=3, and add ID=2
    idTable.Select("ID = 2")[0]["ID"] = 3;
    idTable.Rows.Add(2);

    idTable.AcceptChanges();

    // Delete ID=2, and move old ID=3 to ID=2
    idTable.Select("ID = 2")[0].Delete();
    idTable.Select("ID = 3")[0]["ID"] = 2;

    // Debug GetChanges
    foreach (DataRow row in idTable.GetChanges().Rows)
    {
        if (row.RowState == DataRowState.Deleted)
            Console.WriteLine("Deleted: {0}", row["ID", DataRowVersion.Original]);
        else
            Console.WriteLine("Modified: {0} = {1}", row["ID", DataRowVersion.Original], row["ID", DataRowVersion.Current]);
    }

    // Check GetChanges
    Assert.AreEqual(DataRowState.Deleted, idTable.GetChanges().Rows[0].RowState, "1st row in GetChanges should be deleted row");
    Assert.AreEqual(DataRowState.Modified, idTable.GetChanges().Rows[1].RowState, "2nd row in GetChanges should be modified row");
}

Выходной сигнал:

Modified: 3 = 2
Deleted: 2
TestCase 'GetChanges_NotWorking'
failed: 
  1st row in GetChanges should be deleted row
  Expected: Deleted
  But was:  Modified
Это было полезно?

Решение

Это не ошибка, дело в том, что вы используете идентификаторы (очень) нестандартным способом.Два ответа:

1) Используйте DataTable.GetChanges (DataRowState.Modified) для обработки ваших обновлений по порядку (я думаю, что они будут удалены, изменены, вставлены).Это то, что вы также должны делать с отношениями Master / Detail (до .net 3.0)

2) Переосмыслите свой дизайн, в общем, идентификаторы должны быть неизменяемыми и допускать пробелы и т.д.Это сделает все ваши операции с базой данных намного надежнее и проще.Вы можете использовать другой столбец для поддержания последовательной нумерации для представления пользователю.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top