Это ошибка в API DataTable? Изменения сохраняются / исполняются в «неправильной последовательности» - PullRequest
1 голос
/ 22 июня 2009

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

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

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

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

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

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

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

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

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

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

Обход

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

Мне кажется, я уже сталкивался с подобной «проблемой» со словарями: если вы добавляете некоторые элементы, удаляете их, вставляете их заново, то они не будут в той же последовательности, в которой вы их добавляли (при перечислении словарь).

Вот два теста 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 Ответ

2 голосов
/ 22 июня 2009

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

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

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

...