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

Пакетная обработка

EF Core помогает снизить объем обращений, автоматически объединяя все обновления в единый цикл. Рассмотрим следующий пример.

var blog = context.Blogs.Single(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" });
context.SaveChanges();

Приведенный выше элемент загружает блог из базы данных, изменяет его URL-адрес, а затем добавляет два новых блога. чтобы применить эту возможность, в базу данных отправляются две инструкции INSERT SQL и одна инструкция 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));
}

Групповые обновления

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

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

context.SaveChanges();

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

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

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

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

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

Увы, EF в настоящее время не предоставляет интерфейсы API для выполнения операций обновления. пока они не будут введены, можно использовать необработанные SQL для выполнения операции, в которой важна производительность.

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