Udostępnij za pośrednictwem


Wydajne aktualizowanie

Grupowanie

Program EF Core pomaga zminimalizować przejazdy przez automatyczne dzielenie wszystkich aktualizacji w jedną rundę. Rozważ następujące kwestie:

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();

Powyższe ładuje blog z bazy danych, zmienia adres URL, a następnie dodaje dwa nowe blogi; aby to zastosować, do bazy danych są wysyłane dwie instrukcje SQL INSERT i jedna instrukcja UPDATE. Zamiast wysyłać je pojedynczo, w miarę jak dodawane są wystąpienia blogów, program EF Core śledzi te zmiany wewnętrznie i wykonuje je w jednej operacji, kiedy zostaje wywołane SaveChanges.

Liczba instrukcji, które EF przetwarza w pojedynczej transakcji, zależy od używanego dostawcy bazy danych. Na przykład analiza wydajności wykazała, że przetwarzanie wsadowe jest ogólnie mniej wydajne dla programu SQL Server, gdy są zaangażowane mniej niż 4 instrukcje. Podobnie, korzyści płynące z grupowania zmniejszają się po około 40 instrukcjach dla SQL Server, dlatego w EF Core domyślnie wykonuje się maksymalnie 42 instrukcje w jednej partii, a dodatkowe instrukcje są wykonywane w ramach oddzielnych zapytań do bazy danych.

Użytkownicy mogą również dostosować te progi, aby osiągnąć potencjalnie wyższą wydajność — ale należy dokładnie przeprowadzić test porównawczy przed zmodyfikowaniem następujących wartości:

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

Użyj polecenia ExecuteUpdate i ExecuteDelete, jeśli jest to istotne

Załóżmy, że chcesz dać wszystkim pracownikom podwyżkę. Typowa implementacja tej funkcji w programie EF Core wygląda następująco:

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

Chociaż jest to doskonale prawidłowy kod, przeanalizujmy to, co robi z perspektywy wydajności:

  • W celu załadowania wszystkich odpowiednich pracowników wykonywane jest połączenie z bazą danych; należy pamiętać, że przenosi to wszystkie dane pracowników na poziomie wiersza do klienta, nawet jeśli potrzebne będzie tylko wynagrodzenie.
  • Śledzenie zmian w EF Core tworzy migawki podczas ładowania jednostek, a następnie porównuje te migawki z wystąpieniami, aby ustalić, które właściwości uległy zmianie.
  • Zazwyczaj wykonywane jest drugie zapytanie do bazy danych, aby zapisać wszystkie zmiany (należy pamiętać, że niektórzy dostawcy baz danych dzielą zmiany na wielokrotne zapytania). Mimo że takie zachowanie przetwarzania wsadowego jest znacznie lepsze niż wykonywanie w obie strony dla każdej aktualizacji, program EF Core nadal wysyła instrukcję UPDATE dla każdego pracownika, a baza danych musi wykonać każdą instrukcję oddzielnie.

Począwszy od programu EF Core 7.0, możesz użyć metod ExecuteUpdateAsync i ExecuteDeleteAsync, aby wykonać to samo znacznie wydajniej:

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

Spowoduje to wysłanie następującej instrukcji SQL do bazy danych:

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

Ta UPDATE wykonuje całą operację w jednym przebiegu, bez ładowania ani wysyłania żadnych rzeczywistych danych do bazy danych oraz bez korzystania z mechanizmu śledzenia zmian EF, co nakłada dodatkowe obciążenie. Aby uzyskać więcej informacji, zobacz ExecuteUpdate i ExecuteDelete.

Jeśli używasz starszej wersji programu EF Core, która nie obsługuje jeszcze ExecuteUpdate i ExecuteDeletelub chcesz wykonać złożoną instrukcję SQL, która nie jest obsługiwana przez te metody, nadal możesz użyć zapytania SQL do wykonania operacji:

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

Aby dowiedzieć się więcej o różnicach między SaveChanges oraz ExecuteUpdate/ExecuteDelete, zobacz stronę z podsumowaniem na temat zapisywania danych.