Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Базы данных под .NET > Удаление вновь созданный записи


Автор: albert21 8.3.2006, 17:06
Проблема: В одной из таблиц базы данных создаётся новая запись
Код

        public void InsDisk(DataSetDiskoteka dts, string name, string image, string description, string kategoria, string genre, string type)
        {
            DataRow dr = dts.Tables["Disk"].NewRow();
            dr["Название"] = name;
            dr["Изображение"] = image;
            dr["Описание"] = description;
            dr["Категория"] = kategoria;
            dr["Жанр"] = genre;
            dr["Тип_носителя"] = type;
            dts.Tables["Disk"].Rows.Add(dr);
        }
...
AdapterDisk.Update(dts, "Disk");


Сразу после появления этой записи в гриде, выделяем её и нажимаем кнопку "Удалить":

Код

             DataView dataView = new DataView(dts.Tables["Disk"]);
             foreach (DataGridViewRow r in dataGridDisk.SelectedRows)
             {
                 dataView.Delete(r.Index);

             }
...
AdapterDisk.Update(dts, "Disk");


И вот тут может появиться ошибка! Подчёркиваю МОЖЕТ. Ошибка появляется на последней строке приведённого кода: AdapterDisk.Update(dts, "Disk"); Ругается следующими словами: Concurrency violation: the DeleteCommand affected 0 of the expected 1 records.

Подскажите пожалуйста как решить

Автор: mr.DUDA 8.3.2006, 17:56
Видимо, адаптер толком не понимает что ему делать с записью, которую добавили а потом удалили - добавлять и удалять из БД ? smile

Автор: albert21 8.3.2006, 19:58
В пошаговом режиме после нажатия кнопки "ДОБАВИТЬ" в базе данных действительно появляется запись. Возможно запись в ДатаСет имеет некое специальное состоянние, когда факт обновления не подтверждён! Но это только мои догадки! Есть метод который бы железно подтвердил ранее внесённые изменения в набор данных?

Автор: -ser- 9.3.2006, 05:54
стоп, стоп, стоп.
все не совсем так. не четко описаны намерения, в каком состоянии наши записи, когда мы что-то дальше пытаемся с ними делать.

Цитата

Сразу после появления этой записи в гриде, выделяем её и нажимаем кнопку "Удалить":

здесь очень важно на каком этапе мы хотим удалить запись?
Цитата

Возможно запись в ДатаСет имеет некое специальное состоянние, когда факт обновления не подтверждён!

именно так!

существует несколько промежуточных версий и состояний записи, а именно аж
4 разных версии : current, original, default, proposed.
и 5 разный состояний: added, deleted, detached, modified, unchanged.
и механизм ее удаления зависит от того, в какой версии и в каком состоянии она находится в данный момент. и если мы ее видим в БД или в гриде, после того же добавления, это еще совсем ни о чем не говорит.

существует как минимум 3 основных табличных метода:
Код

AcceptChanges()    
RejectChanges()    
GetChanges()

и 8 основных методов манипуляций с записью:
Код

Add()    
InsertAt()
RemoveAt()    
AcceptChanges()
RejectChanges()
BeginEdit()
CancelEdit()
EndEdit()

использование которых влияет как на версию, так и на состояние записи(сей).

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

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

1. правильнее Adapter.Update делать не на весь ДатаСет, а только на измененные записи. http://forum.vingrad.ru/index.php?showtopic=76977

2. после этого, либо делаем dataSet.AcceptChanges(), либо, для чистоты эксперимента, возьмем и перезальем наш ДатаСет, предварительно его очистив dataSet.Clear();

тоже самое с удалением. проще некуда.

З.Ы. все это относится к ADO.NET, в ADO.NET 2.0 кое-что изменилось, но концепция та же. Что появилось нового, например, можно обойтись вообще без ДатаСета. ДатаТейбл уже стала независимой от ДатаСета и имеет колоссальную функциональность.

Автор: albert21 9.3.2006, 12:05
Написал так:
Код

        public void DelDisk(DataGridView dataGridDisk, DataSetDiskoteka dts, System.Data.OleDb.OleDbDataAdapter adapter)
        {
            foreach (DataGridViewRow r in dataGridDisk.SelectedRows)
            {
                DataRow sRow = dataView[r.Index].Row;
                sRow.Delete();
                sRow.AcceptChanges(); //По идее удаление должно подтвердиться

            }
            if (!dts.HasChanges(DataRowState.Added | DataRowState.Modified | DataRowState.Deleted))
            {
                //MessageBox.Show("Hasn't been changed:" + DataRowState.Added + "  " + DataRowState.Modified);
                return;
            }
            DataSet xDataSet = new DataSet();
            xDataSet = dts.GetChanges(DataRowState.Added | DataRowState.Modified | DataRowState.Deleted);
            adapter.Update(xDataSet, "Disk");
        }


1) В таком варианте удаления не происходит вообще! Нет из ДАТАГРИД всё удаляется, а из базы НЕТ! Виноват в этом sRow.AcceptChanges(); Этот метод даже по названию должен потверждать удаление, он же по факту его отменяет. Чего-то я не пойму!!! smile

2) Без sRow.AcceptChanges(); Изменений никаких, та жа ошибка. Ставил dts.AcceptChanges();, xDataSet.AcceptChanges(); до обновления adapter.Update(xDataSet, "Disk");, результат: из ДАТАГРИД всё удаляется, а из базы НЕТ!

Может я чего-то не догоняю, объясните плиз!!!

Автор: -ser- 10.3.2006, 07:49
все правильно, смотри что происходит, xхх.AcceptChanges() у тебя сбрасывает все флаги изменений, и, соответственно, dts.GetChanges(...) не зафиксирует никаких изменений.
пока не разберешься с версиями и состояниями, просто не используй Accept/RejectChanges. adaptor.Update() cам вызовет AcceptChanges().
Код

{
   foreach (DataGridViewRow r in dataGridDisk.SelectedRows)
               dataView[r.Index].Row.Delete();

//метод с GetChanges и adaptor.Update()
}
//перезалей датасет: adaptor.Fill( dts, "Disk" ). Еще, если у тебя используется только одна таблица, вообще можно не использовать второй параметр в Fill/Update.

Автор: albert21 10.3.2006, 15:51
Всё равно та жа ошибка на Update(). И потом перезалить датасет на мой взгляд не самое лучшая идея с точки зрения производительности. Может хорошая книжка (на русском) электронная по C# + ADO.NET у кого нибудь есть?

Автор: -ser- 12.3.2006, 08:51
на этом форуме специальный раздел посвящен этому, где тонны литературы на любой вкус, уж не ленитесь посмотреть. можешь посмотреть вот это http://www.zipsites.ru/books/almanakh_programmista_tom1/
если честно, сам так и не читал, почти всегда хватает MSDN.

по твоему примеру, попробуй адаптер и основной датасет сделать глобальными.

Автор: albert21 12.3.2006, 11:14
Раздел есть конечно, но там не указано на каком языке книга! smile

Автор: albert21 12.3.2006, 13:56
Проблема заключается в том, что при добавлении новой записи в DataSet эта запись получает значение автоинкремента от DataSet, которое формируется из значения последней записи прочитанной из базы данных увеличенного на единицу. В самой же базе данных значение автоинкремента может быть совершенно другим!!!.

Для того чтобы избавиться от ошибки необходимо повесить обработчик на Adapter:
Код

        private void AdapterDisk_RowUpdating(object sender, System.Data.OleDb.OleDbRowUpdatingEventArgs e)
        {
            int newID = 0;
            .... получаем значение автоинкремента из БД ....

            if ((e.StatementType == StatementType.Insert) || (e.StatementType == StatementType.Delete)) newID = ...присваиваем полученное значение...
            dts.Tables["Disk"].Columns["ID"].AutoIncrementSeed = newID;
            dts.Tables["Disk"].Columns["ID"].AutoIncrementStep = 1;
        }


Теперь вопрос: Как получить значение автоинкремента из БД MS Access?????
ДЛя MS SQL прокатит так вроде:
Код

    System.Data.OleDb.OleDbCommand idCMD = new System.Data.OleDb.OleDbCommand("SELECT @@IDENTITY", oleDbConnection);
            if ((e.StatementType == StatementType.Insert) || (e.StatementType == StatementType.Delete)) newID = Convert.ToInt32(idCMD.ExecuteScalar());

Добавлено @ 13:59
Чуть не забыл!!! Добрый совет: все украшения, типа "НАДО СКРЫТЬ НЕ НУЖНЫЕ ДЛЯ ПОЛЬЗОВАТЕЛЯ ПОЛЯ" делайте в самую последнюю очередь smile

Автор: arilou 14.3.2006, 01:06
Цитата(albert21 @ 12.3.2006, 13:56 Найти цитируемый пост)
Как получить значение автоинкремента из БД MS Access?????

SELECT @@IDENTITY для Аксеса не прокатывает, поэтому надо делать (другого способа не знаю) SELECT MAX(FieldID) FROM Table.

Автор: albert21 14.3.2006, 19:24
Цитата

SELECT @@IDENTITY для Аксеса не прокатывает, поэтому надо делать (другого способа не знаю) SELECT MAX(FieldID) FROM Table


Это тоже не пойдёт, так как записи в БД могут удаляться, а счётчик БД при этом не уменьшается.
Я решил проблему так: убираем счётчик вообще, делаем поле ID просто целочисленным, а при запуске приложения пишем
Код

            dts.Tables["Disk"].Columns["ID"].AutoIncrement = true;
            dts.Tables["Disk"].Columns["ID"].AutoIncrementSeed =Convert.ToInt32( dts.Tables["Disk"].Select(string.Empty, "ID desc" )[0]["ID"])+1;
            dts.Tables["Disk"].Columns["ID"].AutoIncrementStep = 1;


Если кто более удачное решение нароет, скиньте плиз!

Автор: arilou 14.3.2006, 19:29
Цитата(albert21 @ 14.3.2006, 19:24 Найти цитируемый пост)
Это тоже не пойдёт, так как записи в БД могут удаляться, а счётчик БД при этом не уменьшается.

Счетчик и не должен уменьшаться. Он будет все время увеличиваться. Поэтому MAX() как раз наоборот, прокатит.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)