0Pricing
C# Academy · Lekcja

Zapytania kompilowane i wydajność

Zwiększaj wydajność zapytań za pomocą EF.CompileQuery, AsNoTracking i projekcji z użyciem Select.

Zapytania kompilowane i wydajność to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.

Kompilowanie zapytań EF Core

Za każdym razem, gdy EF Core wykonuje zapytanie LINQ, musi przeanalizować drzewo wyrażeń, przetłumaczyć je na SQL i przeprowadzić jego weryfikację. W często wykonywanych ścieżkach ten narzut się kumuluje. EF Core udostępnia kilka narzędzi pozwalających go ograniczyć.

AsNoTracking dla zapytań tylko do odczytu

AsNoTracking() pomija rejestrowanie w module śledzenia zmian. W przypadku zapytań tylko do odczytu, takich jak odpowiedzi API i raporty, jest to największa łatwa do uzyskania poprawa — zwykle zapytania są 2–5 razy szybsze i zużywają mniej pamięci.

// With tracking (default) — for entities you'll modify
var tracked = await _db.Products.FirstOrDefaultAsync(p => p.Id == 1);

// Without tracking — for read-only
var dto = await _db.Products
    .AsNoTracking()
    .Where(p => p.IsActive)
    .Select(p => new ProductDto { Id = p.Id, Name = p.Name })
    .ToListAsync();

Zapytania kompilowane

EF.CompileAsyncQuery wstępnie kompiluje zapytanie LINQ do delegata. Tłumaczenie odbywa się raz podczas uruchamiania aplikacji, a kolejne wywołania całkowicie pomijają etap kompilacji.

// Define once, reuse many times
private static readonly Func<AppDbContext, int, Task<Product?>> GetProductById =
    EF.CompileAsyncQuery(
        (AppDbContext db, int id) =>
            db.Products.AsNoTracking().FirstOrDefault(p => p.Id == id));

// Usage
var product = await GetProductById(_db, 42);

Projekcja z Select na rzecz wydajności

Zawsze należy wykonywać projekcję dokładnie na potrzebne pola. Wybranie obiektu DTO zamiast pełnej encji zmniejsza liczbę pobieranych kolumn, rozmiar danych przesyłanych przez sieć i obciążenie modułu GC.

// Load entire entity (over-fetch)
var all = await _db.Products.ToListAsync();

// Projection: only fetch needed columns
var names = await _db.Products
    .AsNoTracking()
    .Select(p => new { p.Id, p.Name, p.Price })
    .ToListAsync();
// SQL: SELECT Id, Name, Price FROM Products

Wskazówki dotyczące indeksów i optymalizacja surowych zapytań

EF Core respektuje indeksy bazy danych skonfigurowane za pomocą HasIndex(). Indeksy złożone, indeksy filtrowane i dołączone kolumny wpływają na wydajność planu zapytania.

// Configure compound index in OnModelCreating
modelBuilder.Entity<Order>()
    .HasIndex(o => new { o.CustomerId, o.Status })
    .HasFilter("[Status] = 0")  // filtered index
    .IncludeProperties(o => o.PlacedAt);

// The query below will use this index efficiently
var pending = await _db.Orders
    .AsNoTracking()
    .Where(o => o.CustomerId == 5 && o.Status == OrderStatus.Pending)
    .ToListAsync();

Stronicowanie za pomocą Skip i Take

Duże zestawy wyników należy zawsze stronicować. Skip i Take są tłumaczone na SQL OFFSET/FETCH, dzięki czemu tysiące wierszy nie są ładowane do pamięci.

int page = 2, pageSize = 25;

var products = await _db.Products
    .AsNoTracking()
    .Where(p => p.IsActive)
    .OrderBy(p => p.Name)
    .Skip((page - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();
// SQL: ... ORDER BY Name OFFSET 25 ROWS FETCH NEXT 25 ROWS ONLY

Wydajne zliczanie

Należy używać CountAsync() lub AnyAsync() zamiast ładować rekordy tylko po to, aby je zliczyć. AnyAsync() jest jeszcze szybsze niż COUNT, gdy trzeba jedynie sprawdzić istnienie danych.

// Total count for pagination
int totalCount = await _db.Products
    .Where(p => p.IsActive)
    .CountAsync();

// Existence check — faster than Count > 0
bool hasExpensive = await _db.Products
    .AnyAsync(p => p.Price > 1000m);
// SQL: IF EXISTS (SELECT 1 FROM ...) SELECT 1

Masowe wstawianie za pomocą AddRange

Należy używać AddRange, aby dodawać wiele encji jednocześnie. EF Core grupuje instrukcje INSERT, zmniejszając liczbę podróży do bazy danych; rozmiarem partii steruje właściwość MaxBatchSize dostawcy.

var newProducts = Enumerable.Range(1, 1000)
    .Select(i => new Product { Name = $"Product {i}", Price = i * 1.5m })
    .ToList();

_db.Products.AddRange(newProducts);
await _db.SaveChangesAsync();
// Batched: typically 100-1000 rows per round-trip

EF.Functions dla funkcji bazy danych

EF.Functions udostępnia funkcje specyficzne dla bazy danych, takie jak LIKE, części daty i wyszukiwanie pełnotekstowe, które EF Core tłumaczy na SQL zamiast wykonywać w pamięci.

// LIKE pattern search (SQL-side)
var results = await _db.Products
    .Where(p => EF.Functions.Like(p.Name, "%keyboard%"))
    .AsNoTracking()
    .ToListAsync();

// Date difference (SQL-side)
var recentOrders = await _db.Orders
    .Where(o => EF.Functions.DateDiffDay(o.PlacedAt, DateTime.UtcNow) <= 7)
    .ToListAsync();

Diagnozowanie powolnych zapytań

Należy włączyć rejestrowanie zapytań EF Core, aby zobaczyć wygenerowany SQL. W środowisku programistycznym można użyć EnableSensitiveDataLogging(), aby wyświetlać wartości parametrów.

builder.Services.AddDbContext<AppDbContext>(opt =>
    opt.UseSqlServer(connStr)
       .LogTo(Console.WriteLine, LogLevel.Information)
       .EnableSensitiveDataLogging() // dev only!
       .EnableDetailedErrors());     // dev only!

Praktyka: zoptymalizowany endpoint listy

Gotowy do użycia produkcyjnie endpoint listy łączy AsNoTracking, projekcję, indeksy i stronicowanie, zapewniając maksymalną wydajność.

app.MapGet("/products", async (
    AppDbContext db,
    string? search,
    int page = 1, int size = 20) =>
{
    var query = db.Products
        .AsNoTracking()
        .Where(p => p.IsActive);

    if (!string.IsNullOrEmpty(search))
        query = query.Where(p => EF.Functions.Like(p.Name, $"%{search}%"));

    var total = await query.CountAsync();
    var items = await query
        .OrderBy(p => p.Name)
        .Skip((page - 1) * size).Take(size)
        .Select(p => new { p.Id, p.Name, p.Price })
        .ToListAsync();

    return Results.Ok(new { total, items });
});

Szybkie sprawdzenie

Jaka jest główna korzyść z użycia EF.CompileAsyncQuery w porównaniu ze zwykłym zapytaniem LINQ?

Podsumowanie: zapytania kompilowane i wydajność

Najważniejsze informacje:

  • AsNoTracking(): pomija moduł śledzenia zmian w zapytaniach tylko do odczytu — najszybsza łatwa do uzyskania poprawa
  • EF.CompileAsyncQuery: wstępnie kompiluje często wykonywane zapytania do delegatów
  • Projekcja z użyciem Select: pobiera tylko potrzebne kolumny
  • Odpowiednie indeksy (HasIndex) znacznie poprawiają plany zapytań
  • Należy stosować stronicowanie za pomocą Skip/Take i używać AnyAsync do sprawdzania istnienia danych
  • Należy włączyć rejestrowanie zapytań w środowisku programistycznym, aby wykrywać powolny lub nieoczekiwany SQL

Często zadawane pytania

Czy lekcja „Zapytania kompilowane i wydajność” jest bezpłatna?

Tak — pełny tekst „Zapytania kompilowane i wydajność” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Zapytania kompilowane i wydajność”?

Zwiększaj wydajność zapytań za pomocą EF.CompileQuery, AsNoTracking i projekcji z użyciem Select. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C# Academy?

Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Zapytania kompilowane i wydajność”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?

Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Ładowanie eager, lazy i explicit
  2. Surowy SQL, procedury składowane i interpolacja
  3. Zapytania kompilowane i wydajność
  4. Globalne filtry zapytań i encje owned
← Powrót do C# Academy