Поделиться через


Эффективное обновление

Пакетирование

EF Core помогает свести к минимуму количество взаимодействий с базой данных путем автоматической группировки всех обновлений в одном запросе. Рассмотрим следующее:

var blog = await context.Blogs.SingleAsync(b => b.Url == "http://someblog.microsoft.com");
blog.Url = "http://someotherblog.microsoft.com";
context.Add(new Blog { Url = "http://newblog1.microsoft.com" });
context.Add(new Blog { Url = "http://newblog2.microsoft.com" });
await context.SaveChangesAsync();

Приведенный выше блог загружается из базы данных, изменяет его URL-адрес, а затем добавляет два новых блога; Чтобы применить это, в базу данных отправляются две инструкции SQL INSERT и одна инструкция UPDATE. Вместо отправки их по одному, так как добавляются экземпляры блога, EF Core отслеживает эти изменения внутри системы и выполняет их в одном цикле при вызове SaveChanges.

Количество запросов, обрабатываемых EF в одном цикле, зависит от используемого поставщика базы данных. Например, анализ производительности показал, что пакетная обработка обычно менее эффективна для SQL Server, если используются менее 4 инструкций. Аналогичным образом преимущества пакетной обработки ухудшаются после примерно 40 инструкций для SQL Server, поэтому EF Core по умолчанию будет выполнять только до 42 инструкций в одном пакете и выполнять дополнительные инструкции в отдельных раундах.

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

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        @"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
        o => o
            .MinBatchSize(1)
            .MaxBatchSize(100));
}

Использование ExecuteUpdate и ExecuteDelete при необходимости

Предположим, вы хотите дать всем сотрудникам повышение. Типичная реализация для этого в EF Core будет выглядеть следующим образом:

foreach (var employee in context.Employees)
{
    employee.Salary += 1000;
}
await context.SaveChangesAsync();

Хотя это совершенно допустимый код, давайте рассмотрим то, что он делает с точки зрения производительности:

  • Выполняется запрос к базе данных, чтобы загрузить всех релевантных сотрудников; Обратите внимание, что это приводит к передаче всех данных строк сотрудников на клиентскую сторону, даже если потребуется только информация о зарплате.
  • Отслеживание изменений в EF Core создает моментальные снимки при загрузке сущностей и затем сравнивает их с экземплярами, чтобы определить, какие свойства изменились.
  • Как правило, для сохранения всех изменений выполняется повторный обход второй базы данных (обратите внимание, что некоторые поставщики баз данных разделяют изменения на несколько циклов). Хотя это поведение пакетной обработки гораздо лучше, чем выполнение круглой передачи для каждого обновления, EF Core по-прежнему отправляет инструкцию UPDATE на сотрудника, и база данных должна выполнять каждую инструкцию отдельно.

Начиная с EF Core 7.0, можно использовать методы ExecuteUpdateAsync и ExecuteDeleteAsync, чтобы сделать то же самое гораздо эффективнее:

await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));

При этом в базу данных отправляется следующая инструкция SQL:

UPDATE [Employees] SET [Salary] = [Salary] + 1000;

Эта UPDATE выполняет всю операцию в одном цикле без загрузки или отправки фактических данных в базу данных и без использования механизма отслеживания изменений EF, что приводит к дополнительным издержкам. Дополнительные сведения см. в разделе ExecuteUpdate и ExecuteDelete.

Если вы используете старую версию EF Core, которая еще не поддерживает ExecuteUpdate и ExecuteDelete, или хотите выполнить сложную инструкцию SQL, которая не поддерживается этими методами, вы по-прежнему можете использовать SQL-запрос для выполнения операции:

context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");

Дополнительные сведения о различиях между SaveChanges и ExecuteUpdate/ExecuteDeleteсм. на странице обзора о сохранении данных.