Я пытаюсь выполнить потенциально крупномасштабную операцию удаления в одной таблице. (представьте, что 100 000 строк в таблице строк размером 1 м)
Я использую PostgreSQL и EntityFrameworkCore.
Подробности: код приложения имеет предикат для сопоставления и ничего не знает о том, сколько строк потенциально соответствует предикату. Это может быть 0 строк/с или очень большое количество.
Исследования показывают, что EF Core не может эффективно с этим справиться. (т. е. следующий код создает оператор Delete для каждой строки!)
Using (var db = new DbContext)
var queryable = db.Table.AsQueryable()
.Where(o => o.ForeignKey == fKey)
.Where(o => o.OtherColumn == false);
db.Table.RemoveRange(queryable);
await db.SaveChangesAsync();
Итак, вот SQL, который я бы предпочел запускать в виде пакетной операции:
delete from Table
where ForeignKey = 1234
and OtherColumn = false
and PK in (
select PK
from Table
where ForeignKey = 1234
and OtherColumn = false
limit 500
)
Существуют библиотеки расширений, но мне еще предстоит найти активную, поддерживающую Postgres. В настоящее время я выполняю необработанный sql выше через EF Core.
Это приводит к паре вопросов:
🤔 А знаете ли вы, что...
PostgreSQL поддерживает шифрование данных и обеспечение безопасности.
Отказ от ответственности: Я владелец проекта Сущность Платформа Плюс
Ваш сценарий выглядит как то, с чем могут справиться наши функции Batch Delete
: https://entityframework-plus.net/batch-delete
Using (var db = new DbContext)
var queryable = db.Table.AsQueryable()
.Where(o => o.ForeignKey == fKey)
.Where(o => o.OtherColumn == false);
queryable.Delete();
Сущности не загружаются в приложение, и выполняется только SQL, как вы указали.
Я думаю, вы пытаетесь сделать что-то, для чего не следует использовать EntityFrameworkCore. Цель EntityFrameworkCore — иметь удобный способ перемещения данных между приложением .Net-Core и базой данных. Типичный useway - это один или небольшое количество объектов. Для массовых операций есть несколько nuget-пакетов. Существует пакет это для вставки и обновления с помощью postgres. Эта статья автора объясняет, как он использует временные таблицы и команду postgres COPY для выполнения массовых операций. Это показывает нам способ массового удаления строк по идентификатору:
var toDelete = GetIdsToDelete();
using (var conn = new NpgsqlConnection(connectionString))
{
conn.Open();
using ( var cmd = conn.CreateCommand())
{
cmd.CommandText =("CREATE TEMP TABLE temp_ids_to_delete (id int NOT NULL) ON COMMIT DROP ");
cmd.Prepare();
cmd.ExecuteNonQuery();
}
using (var writer = conn.BeginBinaryImport($"COPY temp_ids_to_delete (id) FROM STDIN (FORMAT BINARY)"))
{
foreach (var id in toDelete)
{
writer .StartRow();
writer .Write(id);
}
writer .Complete();
}
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "delete from myTable where id in(select id from temp_ids_to_delete)";
cmd.Prepare();
cmd.ExecuteNonQuery();
}
conn.Close();
С некоторыми небольшими изменениями это может быть более обобщенным.
Но вы хотите сделать что-то другое. Вы не хотите перемещать данные или информацию между приложением и базой данных. Вы хотите использовать efcore для создания slq-процедуры на лету и запускать ее на сервере. Проблема в том, что ядро ef на самом деле не предназначено для этого. Но, возможно, есть способы обойти это. Один из способов, который я мог бы придумать, - использовать ef-core для создания запроса, получить строку запроса, а затем вставить эту строку в другую строку sql для запуска на сервере. Получить строку запроса в настоящее время непросто, но, очевидно, это будет с EF Core 5.0. Тогда вы можете сделать это:
var queryable = db.Table.AsQueryable()
.Where(o => o.ForeignKey == fKey)
.Where(o => o.OtherColumn == false);
var queryString=queryable.ToQueryString();
db.Database.ExecuteSqlRaw("delete from Table where PK in("+queryString+")" )
И да, это ужасно хаки, и я бы не рекомендовал этого. Я бы рекомендовал писать процедуры и функции на сервере базы данных, потому что это не то, для чего следует использовать ef-core. И тогда вы все еще можете запускать эти функции из ef-core и передавать параметры.
Я бы предложил использовать временные таблицы для выполнения такой операции. Вы должны создать зеркальную временную таблицу, массово добавить записи для сохранения или удаления в временную таблицу, а затем выполнить операцию удаления, которая ищет записи в/не в этой временной таблице. Попробуйте использовать такую библиотеку, как PgPartner, чтобы очень легко выполнять массовое добавление и создание временных таблиц.
Проверьте PgPartner: https://www.nuget.org/packages/PgPartner/
https://github.com/SourceKor/PgPartner