Это ошибка в DataTable API?Изменения сохраняются / выполняются в “неправильной последовательности”
-
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) Переосмыслите свой дизайн, в общем, идентификаторы должны быть неизменяемыми и допускать пробелы и т.д.Это сделает все ваши операции с базой данных намного надежнее и проще.Вы можете использовать другой столбец для поддержания последовательной нумерации для представления пользователю.