Массовое удаление в LINQ to Entities

Есть ли способ массового удаления группы объектов, соответствующих заданному запросу, в LINQ или LINQ-to-Entities? Единственные ссылки, которые я могу найти, устарели, и кажется глупым перебирать и вручную удалять все объекты, которые я хочу удалить.


person Benjamin Pollack    schedule 15.05.2009    source источник


Ответы (14)


Вопрос старый (еще до того, как EF5 существовал). Для всех, кто использует EF5, EntityFramework.Extended сделает это в мгновение ока.

person Shaul Behr    schedule 22.11.2012

Некоторое время назад я написал серию блогов из 4 частей (Parts 1, 2, 3 и 4), охватывающий выполнение массовых обновлений (с помощью одной команды) в Entity Framework.

Хотя основное внимание в этой серии было уделено обновлению, вы определенно могли использовать соответствующие принципы для удаления.

Итак, у вас должно получиться написать что-то вроде этого:

var query = from c in ctx.Customers
            where c.SalesPerson.Email == "..."
            select c;

query.Delete();

Все, что вам нужно сделать, это реализовать метод расширения Delete (). Посмотрите серию сообщений, чтобы узнать, как ...

Надеюсь это поможет

person Alex James    schedule 15.05.2009
comment
Отличная серия. Написание основанного на этом метода расширения Delete () будет тривиальным делом. - person Benjamin Pollack; 15.05.2009
comment
Было бы неплохо иметь здесь образец кода, если он у кого-нибудь есть! - person jocull; 04.01.2012
comment
Здесь можно найти множество методов расширения (включая пакетное удаление): github.com/loresoft/EntityFramework. Расширенный - person Soliah; 19.04.2012
comment
Помните, что github.com/loresoft/EntityFramework.Extended зависит от EF5, если вы используете Nuget. Консоль диспетчера пакетов для его установки, она установит EF5 без запроса. - person Paul Zahra; 02.10.2012
comment
На самом деле это не помогает - я хотел бы увидеть рабочий пример команды «Удалить» и не гадать, как реализовать свой собственный в производственном коде и, возможно, что-то напортачить. -1 - person nuzzolilo; 06.02.2013
comment
Хм ... так что ответ на вопрос - перенаправить на 4 сообщения в блоге вместо того, чтобы представлять конкретный ответ аудитории. - person DeepSpace101; 13.04.2013
comment
Пакет nuget от loresoft не работает должным образом в некоторых случаях и, похоже, больше не поддерживается. В итоге я удалил его из своих проектов и заменил его хранимыми процедурами. - person Micaël Félix; 06.10.2015
comment
Предоставленные ссылки больше никуда не ведут. - person Alex Zhukovskiy; 01.04.2020

Ответы, которые я здесь вижу, - это Linq to Sql

DeleteAllOnSubmit является частью System.Data.Linq и ITable, которые являются Linq to Sql

Это невозможно сделать с помощью Entity Framework.

Сказав все это, у меня еще нет решения, но я отправлю его, когда сделаю

person Phil Strong    schedule 30.03.2010

Для тех, кто использует EF6 и хочет выполнить строковый SQL-запрос для удаления:

using (var context = new DatabaseEntities())
{
    // delete existing records
    context.Database.ExecuteSqlCommand("DELETE FROM YOURTABLE WHERE CustomerID = @id", idParameter);
}
person Uriil    schedule 08.07.2014
comment
Это сработало для меня в EF 5, но мне пришлось использовать @ p0 для параметра. Что приятно, так это то, что он обеспечивает проверку безопасного типа параметров в сгенерированном sql: поэтому в EF5 это будет работать: context.Database.ExecuteSqlCommand (УДАЛИТЬ ИЗ ВАШЕЙ ТАБЛИЦЫ, ГДЕ CustomerID = @ p0, idParameter); \ @ p1 для следующего параметра и т. д. - person Nathan Prather; 07.11.2014
comment
ТАБЛИЦУ можно получить следующим образом: stackoverflow.com/a/45671666/1016343 С помощью этого вы можете написать функцию, которая принимает таблица как параметр типа. Тогда у вас будет обычная массовая операция для удаления таблиц. - person Matt; 02.03.2021

RemoveRange был введен в EF6, он может удалять список объектов. Очень просто.

var origins= (from po in db.PermitOrigins where po.PermitID == thisPermit.PermitID select po).ToList();
db.PermitOrigins.RemoveRange(origins);
db.SaveChanges();
person Jarrette    schedule 27.02.2017
comment
Хотя этот фрагмент кода может решить вопрос, включение объяснения действительно помогает улучшить качество вашего поста. Помните, что вы отвечаете на вопрос для читателей в будущем, и эти люди могут не знать причины вашего предложения кода. - person DimaSan; 27.02.2017

Я знаю метод DeleteAllOnSubmit для любой контекст данных, который удалит все записи в запросе. Должна быть некоторая оптимизация, поскольку удаляется много объектов. Хотя я не уверен.

person Hemant    schedule 15.05.2009
comment
На самом деле никакой оптимизации не производится. Сгенерированный SQL перечисляет все объекты, соответствующие вашему запросу, а затем вручную выполняет итерацию по ним, чтобы удалить их. Конечно, итерация происходит в БД, а не в вашем коде, но вы по-прежнему без необходимости создаете набор результатов просто для удаления его содержимого - все еще намного хуже, чем простое DELETE FROM table WHERE foo = bar, которое не создает результата набор и накрывает стол только один раз. - person Benjamin Pollack; 15.05.2009

Я не уверен, насколько это будет эффективно, но вы можете попробовать что-то вроде этого:

// deletes all "People" with the name "Joe"
var mypeople = from p in myDataContext.People
               where p.Name == "Joe";
               select p;
myDataContext.People.DeleteAllOnSubmit(mypeople);
myDataContext.SubmitChanges();
person Scott Anderson    schedule 15.05.2009
comment
Это все равно приводит к перебору всех элементов, соответствующих запросу; он просто делает это в БД, а не в вашем коде. Более эффективное, но все же далеко не идеальное решение. - person Benjamin Pollack; 15.05.2009
comment
Единственный другой способ, который я мог придумать, - это выполнить myDataContext.ExecuteCommand (DELETE ...) ;. Это тоже далеко не идеально, но это сработает. - person Scott Anderson; 15.05.2009

Вы могли бы написать сохраненную процедуру, которая выполняет удаление и вызывать ее из LINQ. Удаление на основе набора, вероятно, в целом быстрее, но если оно затрагивает слишком много записей, вы можете вызвать проблемы с блокировкой, и вам может потребоваться гибрид перебора наборов записей (возможно, 2000 за раз, размер зависит от вашей структуры базы данных, но 2000 - это начальное место, если вы обнаружите, что дельта на основе набора занимает так много времени, что влияет на другое использование таблицы) для выполнения удаления.

person HLGEM    schedule 15.05.2009

Удаление данных через Entity Framework зависит от использования метода DeleteObject. Вы можете вызвать этот метод в EntityCollection для класса сущности, который вы хотите удалить, или в производном ObjectContext. Вот простой пример:

NorthwindEntities db = new NorthwindEntities();

IEnumerable<Order_Detail> ods = from o in db.Order_Details
                                where o.OrderID == 12345                                    
                                select o;

foreach (Order_Detail od in ods) 
    db.Order_Details.DeleteObject(od);

db.SaveChanges();
person Amin    schedule 11.11.2012
comment
Однако это не массовое удаление. - person nuzzolilo; 06.02.2013

В этом примере я получаю записи для удаления и одну за другой прикрепляю их к набору результатов, а затем запрашиваю их удаление. Затем у меня есть 1 сохранение изменений.

    using (BillingDB db = new BillingDB())
    {
      var recordsToDelete = (from i in db.sales_order_item
                  where i.sales_order_id == shoppingCartId
                  select i).ToList<sales_order_item>();

      if(recordsToDelete.Count > 0)
      {
        foreach (var deleteSalesOrderItem in recordsToDelete)
        {                  
            db.sales_order_item.Attach(deleteSalesOrderItem);
            db.sales_order_item.Remove(deleteSalesOrderItem);                  
        }
        db.SaveChanges();
      } 
    }
person Demodave    schedule 10.02.2015

 context.Entity.Where(p => p.col== id)
               .ToList().ForEach(p => db.Entity.DeleteObject(p));

это самый быстрый способ удалить запись из БД с помощью EF

person Anurag Deokar    schedule 09.05.2015

Я бы сделал что-то вроде:

var recordsToDelete = (from c in db.Candidates_T where c.MyField == null select c).ToList<Candidates_T>();
if(recordsToDelete.Count > 0)
{
    foreach(var record in recordsToDelete)
    {
        db.Candidate_T.DeleteObject(record);
        db.SaveChanges();
    }
}   

Я не думаю, что есть способ сделать это без цикла, поскольку Entity Framework работает с Entities, и в большинстве случаев это означает сбор объектов.

person j.rmz87    schedule 19.09.2014
comment
Вы также можете поделиться тем, что сделали. Спасибо. - person j.rmz87; 10.02.2015
comment
@G Jeny Ramirez Добавил свое решение. - person Demodave; 10.02.2015
comment
@GJennyRamirez также в вашем примере вы сохраняете изменения несколько раз, и я думаю, вы можете вытащить это из цикла foreach и выполнить один раз - person Demodave; 11.02.2015

В текущем EF не реализована массовая операция.

Это просто так, как это было разработано командой entity framework. Декомпилятор ясно показывает, что делает EF внутри:

public void DeleteAllOnSubmit<TSubEntity>(IEnumerable<TSubEntity> entities) 
where TSubEntity : TEntity
{
    if (entities == null)
    {
        throw Error.ArgumentNull("entities");
    }
    CheckReadOnly();
    context.CheckNotInSubmitChanges();
    context.VerifyTrackingEnabled();
    foreach (TSubEntity item in entities.ToList())
    {
        TEntity entity = (TEntity)(object)item;
        DeleteOnSubmit(entity);
    }
}

Как видите, внутри EF проходит цикл по всем элементам таблицы - материализуется в памяти путем вызова .ToList().

Если вы по-прежнему хотите использовать возможности EF из коробки, а не отправлять команду SQL, вы все равно можете облегчить себе жизнь с помощью небольшого вспомогательного метода. Синтаксис

ctx.Table1.DeleteAllOnSubmit(ctx.Table1);
ctx.Table2.DeleteAllOnSubmit(ctx.Table2);
ctx.Table3.DeleteAllOnSubmit(ctx.Table3);
ctx.SubmitChanges();

на мой взгляд выглядит не очень красиво.

Вот пример, который я написал в LinqPad, который немного упрощает его:

void Main()
{
    var ctx = this;

    void DeleteTable<T>(System.Data.Linq.Table<T> tbl, bool submitChanges = false)
    where T : class
    {
        tbl.DeleteAllOnSubmit(tbl);
        if (submitChanges) ctx.SubmitChanges();
    }
    
    DeleteTable(ctx.Table1);
    DeleteTable(ctx.Table2);
    DeleteTable(ctx.Table3);
        
    ctx.SubmitChanges();
}

Если вы проводите тестирование и вам нужно удалить много таблиц, с этим синтаксисом намного проще справиться. Другими словами, это некий синтаксический сахар для вашего удобства. Но имейте в виду, что EF по-прежнему перебирает все объекты внутри и в памяти, что может быть очень неэффективным, если это много данных.

person Matt    schedule 02.02.2021
comment
Это LINQ-to-SQL, ошибка, часто возникающая при работе с Linqpad. - person Gert Arnold; 02.02.2021
comment
Тем не менее, вы предлагаете лишь небольшой метод, который дает некоторый синтаксический сахар, но не решает проблему. Не совсем актуально ИМО. - person Gert Arnold; 02.02.2021
comment
@GertArnold - Этот критик в основном обращается к команде entity framework, которая не предоставила такую ​​функцию для действительно массового удаления данных, как я указал в дизассемблированном коде. Но мы действительно ищем здесь решение. Написанная мною небольшая функция была бы более полезной, если бы я мог определить имя таблицы как строку для вызова оператора удаления SQL. У тебя есть идеи, Герт, как это можно получить? Тогда я мог бы изменить функцию, и она действительно выполнила бы массовую операцию с помощью SQL DELETE. - person Matt; 03.02.2021
comment
Это все не к делу. EF не поддерживает массовое удаление, точка. Есть много способов создать код, который немного проще выполняет точечное удаление. Все неактуально. Не поняли этого и другие ответившие. - person Gert Arnold; 03.02.2021
comment
@GertArnold - Согласен, я более четко сформулировал этот момент в своем ответе. - person Matt; 03.02.2021
comment
Возвращаясь к моему первому комментарию. Не тратьте силы на доказательство того, что версия RemoveRange EF также не выполняет массовое удаление. - person Gert Arnold; 03.02.2021
comment
@GertArnold - Вы правы, и я вам за это благодарен. Я надеюсь, что когда-нибудь у EF появятся реальные оптимизированные массовые операции ... - person Matt; 03.02.2021

person    schedule
comment
+1 - Приятно видеть пример кода того, как выполнить код SQL с помощью EF - person Carlos P; 23.02.2012
comment
Я понимаю, что это, вероятно, единственный способ сделать это, за исключением создания хранимой процедуры, но это похоже на обман =). Теперь, когда я использую это, у меня возникает соблазн использовать его в нескольких других местах, чтобы избавиться от причудливости EF, например, сложных левых объединений и группировки ..... :) - person Losbear; 19.12.2012
comment
+! ... при использовании DB вы обнаружите, что нужный вам инструмент - это отвертка .. EF - это просто еще один молоток. - person gbjbaanb; 12.08.2013
comment
Небольшой недостаток: теперь у вас есть команда базы данных, которая отключена от среды разработки. Он не строго типизирован, поэтому изменение базы данных для столбцов в этом SQL не будет выделено в Visual Studio. - person Ian; 27.04.2016