Как удалить 1000 строк с помощью EF6?

Я использую Entity Framework 6.

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

   var testList = _testService.GetTests(1, userId).ToList();
   testList.ForEach(_obj => _uow.Tests.Delete(_obj));
   _uow.Commit();

У меня есть еще одна таблица с информацией о вопросе под названием «Вопросы». Я хотел бы сделать то же самое, но в этой таблице более 1000 строк. Если я перечислю их все, а затем сделаю 1000 удалений, это будет не очень эффективно.

Такое удаление вопросов происходит не очень часто. Есть ли у кого-нибудь предложение о том, как я могу это сделать. Должен ли я сделать 1000 удалений. Нормально ли делать такие вещи с помощью EF?


person Samantha J T Star    schedule 05.02.2014    source источник
comment
Также возможно удалить объект, не извлекая его — с некоторыми оговорками — blogs.msdn.com/b/alexj/archive/2009/03/27/   -  person Colin    schedule 05.02.2014


Ответы (3)


Насколько я знаю, EF 6 представил параметр .RemoveRange() в вашем DbContext. Короче говоря, вы можете сделать что-то вроде следующего:

var db = new MyDbContext();
var itemsToDelete = db.MyTable.Where(x=>!x.active);
db.MyTable.RemoveRange(itemsToDelete);
db.SaveChanges();

Таким образом, вместо того, чтобы выполнять какие-либо операции foreach, вы можете использовать этот новый метод расширения. С вашим контекстом Unit Of Work у вас может быть перегрузка вашего метода Delete, который принимает IEnumerable (?*) вместо одного объекта Test, как ваш текущий метод. Эта новая перегрузка должна вызывать функцию RemoveRange() в DbContext.

?* - Это зависит от того, что возвращает GetTests(), но я думаю, что IEnumerable<> охватывает как IList<>, так и IQueryable<>

Изменить

Пара комментариев. Во-первых, я бы не стал вызывать .ToList() перед выдачей RemoveRange, так как вы не хотите фактически получать элементы для своей службы. Это должно помочь сократить время выполнения. Во-вторых, вы правы в том, что вы все равно будете выдавать 1000 операторов удаления. Однако повышение производительности достигается за счет отказа от вызова ChangeTracker в EF для каждого отдельного элемента, который вы удаляете из файла DbSet. Из журнала MSDN:

AddRange и RemoveRange Как упоминалось ранее, AddRange и RemoveRange являются вкладом члена сообщества Zorrilla. Каждый метод принимает в качестве параметра перечисляемый объект одного типа. В первом примере кода в разделе совместного использования DbTransactions я использовал AddRange при передаче массива экземпляров Casino:

context.Casinos.AddRange(new[] {казино1, казино2}); Эти методы выполняются намного быстрее, чем добавление или удаление одного объекта за раз, поскольку по умолчанию Entity Framework вызывает DetectChanges в каждом методе добавления и удаления. С помощью методов Range вы можете обрабатывать несколько объектов, в то время как DetectChanges вызывается только один раз, что значительно повышает производительность. Я тестировал это, используя пять, 50, 500, 5000 и даже 50 000 объектов, и, по крайней мере, в моем сценарии нет ограничений на размер массива — и это впечатляюще быстро! Имейте в виду, что это улучшение имеет значение только для того, чтобы контекст воздействовал на объекты, и не имеет отношения к SaveChanges. Вызов SaveChanges по-прежнему выполняет только одну команду базы данных за раз. Таким образом, хотя вы можете быстро добавить 50 000 объектов в контекст, вы все равно получите 50 000 команд вставки, выполняемых по отдельности при вызове SaveChanges — вероятно, это не то, что вы хотите делать в реальной системе.

С другой стороны, велись долгие дискуссии о реализации поддержки массовых операций без необходимости отслеживания объектов с помощью EF (bit.ly/16tMHw4), а также о пакетных операциях, позволяющих отправлять несколько команд вместе за один вызов базы данных. (bit.ly/PegT17). Ни одна из функций не вошла в первоначальный выпуск EF6, но обе важны и запланированы для будущего выпуска.

Если вы действительно хотите выполнить только одну команду базы данных, вам подойдет либо хранимая процедура с использованием необработанных операторов SQL, поскольку EntityFramework не поддерживает массовые транзакции. Однако использование элементов RemoveRange и AddRange (особенно если, как вы сказали, нечасто) сэкономит вам много времени по сравнению с вызовом Remove() в цикле foreach.

person Tommy    schedule 05.02.2014
comment
Спасибо. Я посмотрю на это сейчас. - person Samantha J T Star; 05.02.2014
comment
Это кажется хорошим вариантом, но я не понимаю, как это работает. У меня есть ощущение, что он все равно выдаст 1000 удалений базы данных. - person Samantha J T Star; 05.02.2014
comment
@SamanthaJ - я добавил некоторые подробности о том, как это на самом деле работает. Однако вы правы, поскольку метод оптимизирован, вы увидите прирост производительности по сравнению с методом foreach. Если вам нужна только одна команда, вы вынуждены использовать хранимые процедуры или создавать собственный синтаксис SQL и отправлять его на сервер. - person Tommy; 05.02.2014

Встроенный в Entity Framework метод .RemoveRange() по-прежнему извлекает записи в памяти и выдает цикл удаления X для всех из них.

Если вы не хотите писать любой SQL для удаления, особенно если сложно выбрать объекты для удаления

Библиотека Entity Framework Plus предлагает методы пакетного удаления-обновления выдача только одной единственной команды.

// Deleting
context.Users
  .Where(u => u.FirstName == "firstname")
  .Delete();

Текущие ограничения Entity Framework заключаются в том, что для обновления или удаления объекта необходимо сначала извлечь его в память. Теперь в большинстве случаев это нормально. Однако есть некоторые senerios, где производительность может пострадать. Кроме того, при однократном удалении объект должен быть получен до того, как его можно будет удалить, что требует двух обращений к базе данных. Пакетное обновление и удаление избавляет от необходимости извлекать и загружать объект перед его изменением.

person Anestis Kivranoglou    schedule 16.02.2016
comment
Просто к вашему сведению, так как я довольно часто получаю сообщения об этом вопросе и ответе ... Я просто пошел, взял и посмотрел на эту библиотеку, и я думаю, что следует проявлять некоторую осторожность. Он не обновлялся с июля 2015 года и в настоящее время имеет 117 открытых вопросов. Я не уверен, что он все еще поддерживается. - person Tommy; 24.03.2016
comment
меня это беспокоит, возможно, теперь они вносят свой вклад напрямую в EF, теперь это открытый исходный код? - person chrispepper1989; 14.11.2016
comment
Просто чтобы помочь другим, которые могут захотеть пойти по пути EF-Plus; они повторно работали над ним, и он был обновлен 5 дней назад. Похоже, он снова в деле обслуживания. github.com/zzzprojects/EntityFramework-Plus - person bradykey; 25.10.2018

Я сделал несколько тестов, используя EF6 и Sql Server Profiler.

Использование .RemoveRange()

Сначала он извлекает все записи для удаления из базы данных.

exec sp_executesql N'SELECT [Extent1].[Id] AS [Id], [Extent1].[IdOrder] AS [IdOrder], [Extent1].[Name] AS [Name], [Extent1].[Partita] AS [ Partita], FROM [dbo].[MyTable] AS [Extent1] WHERE [Extent1].[IdOrder] = @p__linq__0',N'@p__linq__0 varchar(8000)',@p__linq__0='0cb41f32-7ccb-426a-a159- b85a4ff64c29'

Затем он запускает команду удаления N в базу данных

exec sp_executesql N'DELETE [dbo].[MyTable] WHERE ([Id] = @0)',N'@0 varchar(50)',@0='ffea29aa-8ba5-4ac9-871b-3f5979180006'

Х 1000 раз

Это происходит также с использованием и IQueriable

Использование расширенной библиотеки Entity Framework

Он запускает только одну команду в базу данных

exec sp_executesql N'DELETE [dbo].[MyTable] FROM [dbo].[MyTable] AS j0 INNER JOIN ( SELECT 1 AS [C1], [Extent1].[Id] AS [Id] FROM [dbo].[MyTable ] AS [Extent1] WHERE [Extent1].[IdOrder] = @p__linq__0) AS j1 ON (j0.[Id] = j1.[Id])',N'@p__linq__0 nvarchar(36)',@p__linq__0=N' 0cb41f32-7ccb-426a-a159-b85a4ff64c29 '

person Marco Staffoli    schedule 15.05.2017
comment
Как отмечено в ответе выше, ваше тестирование подтверждает, что EF не выполняет массовые транзакции. Я по-прежнему разделяю свои опасения по поводу использования расширенной библиотеки EF, так как она не обновлялась с июля 2015 года, и в настоящее время насчитывается около 145 нерешенных проблем. ИМХО, лучшим методом по-прежнему было бы либо вызвать хранимую процедуру, либо заставить EF выполнить оператор SQL, который вы хотите напрямую, например ctx.Database.ExecuteSqlCommand("DELETE FROM MyTable WHERE myColumn = someValue"); Источник: msdn.microsoft.com/en-us/library/jj592907%28v=vs.113%29.aspx - person Tommy; 17.05.2017
comment
Я согласен с тобой. Меня пугает, что по какой-то причине эта библиотека расширений выдаст неверный перевод в sql и удалит неправильные записи. - person Marco Staffoli; 17.05.2017