Удаление записей из базы данных

86

»» В ДАННОЙ СТАТЬЕ ИСПОЛЬЗУЕТСЯ ИСХОДНЫЙ КОД ДЛЯ ПРИМЕРОВ

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

// Создание ObjectContext
            NorthwindEntities context = new NorthwindEntities();

            // Получить детали заказа 10248
            IQueryable<Order_Detail> ods = from o in context.Order_Details
                                           where o.OrderID == 10248
                                           select o;

            // Вывести результаты
            Console.WriteLine("Перед удалением: ");
            foreach (Order_Detail od in ods) {
                Console.WriteLine("Детали заказа {0}, {1}, {2}",
                    od.ProductID, od.UnitPrice, od.Quantity);
            }

            // Удалить детали первого заказа
            context.DeleteObject(ods.First());

            // Сохранить изменения
            context.SaveChanges();

            // Вывести результаты запроса
            Console.WriteLine("\nПосле удаления: ");
            foreach (Order_Detail od in ods) 
                Console.WriteLine("Детали заказа {0}, {1}, {2}",
                   od.ProductID, od.UnitPrice, od.Quantity);

В этом коде запрашиваются все сущностные объекты Order_Detail со значением OrderID, равным 10248. Затем с помощью метода First первый из них выбирается и передается в качестве аргумента методу ObjectContext.DeleteObject. Для сохранения изменений вызывается метод ObjectContext.SaveChanges, который отправляет команду удаления в базу данных.

Класс EntitySet также имеет метод DeleteObject, а это значит, что можно получить тот же эффект, что и в предыдущем примере, с использованием сущностного объекта, представляющего таблицу, из которой нужно удалить запись:

...

// Удалить детали первого заказа
context.Order_Details.DeleteObject(ods.First());
            
...

В этом коде выполняется тот же запрос, что и в предыдущем примере, но здесь используется метод EntitySet.DeleteObject. Компиляция и запуск кода этих примеров даст одинаковый результат:

Удаление записи с использованием сущностного класса

Удаление связанных объектов

Удаление сущностного объекта в Entity Framework приводит к удалению автоматически связанных с ним объектом. При удалении сущностного объекта, имеющего связанные с ним объекты, следует проявлять осторожность: в зависимости от схемы базы данных, могут либо появиться "висячие" данные (данные, внешний ключ которых ссылается на более несуществующий первичный ключ), либо произойти исключение о нарушении ограничения схемы.

Ниже демонстрируется то, что случится, когда объект удаляется без обработки связанных с ним объектов. В данном случае предпринимается попытка удалить сущностный объект Order:

// Создание ObjectContext
            NorthwindEntities context = new NorthwindEntities();

            // Запросить первый заказ для LAZYK
            Order firstOrder = context.Orders
                .Where(o => o.CustomerID == "LAZYK")
                .Select(o => o)
                .First();

            // Удалить заказ
            context.DeleteObject(firstOrder);

            // Сохранить изменения
            context.SaveChanges();

Компиляция и запуск этого кода приведет к генерации следующего исключения:

Unhandled Exception: System.Data.UpdateException: 
An error occurred while updating the entries. See the inner exception
for details. ---> System.Data.SqlClient.SqlException: The DELETE statement
conflicted with the REFERENCE constraint "FK Order Details_Orders". 
The conflict occurred in database "Northwind", table "dbo.Order Details", column 'OrderID'. 
The statement has been terminated.

Необработанное Исключение: System.Data.UpdateException: 
Во время обновления записей возникла ошибка. Для получения сведений 
см. внутреннее исключение. ---> System.Data.SqlClient.SqlException: Оператор DELETE 
конфликтует со ССЫЛОЧНЫМ ограничением "FK_Order Details_Orders". 
Конфликт произошел в базе данных "Northwind", таблица "dbo.Order Details", столбец 'OrderID'.
Оператор принудительно завершен.

Что же произошло? Было нарушено ограничение схемы базы данных. Исключение сообщает о том, что в таблице Order Details имеется ограничение по имени FK Order Details_Orders. Опция Enforce Foreign Key Constraint (Навязывать ограничение внешнего ключа) установлена в Yes (Да), а это означает, что удалить Order не получится, пока есть связанные с ним записи в таблице Order Details.

Существуют два подхода к безопасному удалению связанных объектов. Это можно сделать вручную или воспользоваться каскадным удалением в базе данных, и тогда сущностная модель данных обработает их автоматически. Мы предпочитаем автоматический подход, но поскольку бывают случаи, когда невозможно модифицировать схему базы данных, приходится применять ручной подход. Ниже продемонстрированы оба метода.

Ручное удаление связанных объектов

Возможно, простейший способ удаления связанных объектов состоит в простой передаче каждого из них методу ObjectContext.DeleteObject. При этом необходимо соблюдать осторожность. Во-первых, следует убедиться, что порядок их удаления не нарушает ограничений схемы. Например, если требуется удалить Order, то сначала следует удалить связанные объекты Order_Detail, а только потом удалять Order. Если сделать наоборот, получится исключение вроде того, что видели ранее.

Во-вторых, нужно убедиться, что они удалены все — что-то оставлять нельзя, иначе будет получено исключение или появится несколько строк "висячих" данных.

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

Таким образом, чтобы вручную удалить Order, сначала понадобится удалить все связанные с ним объекты Order_Detail. Ниже показано, как это делается:

// Создание ObjectContext
            NorthwindEntities context = new NorthwindEntities();

            // Запросить первый заказ для LAZYK
            Order firstOrder = context.Orders
                .Where(o => o.OrderID== 10248)
                .Select(o => o)
                .First();

            // Удалить объекты Order_Detail этого объекта
            foreach (Order_Detail od in firstOrder.Order_Details.ToArray())
            {
                Console.WriteLine("Deleting order detail {0}, {1}, {2}, {3}",
                    od.OrderID, od.ProductID, od.UnitPrice, od.Quantity);
                context.DeleteObject(od);
            }

            // Удалить заказ
            context.DeleteObject(firstOrder);

            // Сохранить изменения
            context.SaveChanges();

В этом примере запрашивается объект Order со значением свойства OrderID, равным 10248. Он выбран именно потому, что в базе данных с ним связано более одного Order_Detail. Перед удалением собственно Order объекты Order Detail перечисляются и удаляются один за другим.

Обратите внимание на вызов метода ToArray на Order_Details EntityCollection и перечисление результата. Если этого не сделать, пришлось бы удалять объекты из обрабатываемого перечисления и тогда после удаления первого объекта Order_Details возникло бы исключение.

Каскадное удаление связанных объектов

Другой способ обработки связанных объектов предусматривает применение каскадного удаления. Каскадное удаление означает, что в случае удаления из базы данных записи, такой как Order, связанные записи, имеющие с ней отношение внешнего ключа, вроде записей из таблицы Order Details, также будут удалены автоматически.

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

Подробности использования каскадного удаления ищите на форуме MSDN.

Пройди тесты
Лучший чат для C# программистов