IEnumerable vs IQueryable w EF Core – wydajność i pułapki
Masz działający kod w EF Core, wrzucasz go na produkcję… i nagle aplikacja zaczyna zużywać ogromne ilości RAM, a baza danych dostaje zadyszki?
W wielu przypadkach przyczyną jest jedna, pozornie niewinna decyzja:
👉 użycie IEnumerable zamiast IQueryable.
W tym artykule pokażę Ci realny problem produkcyjny, wyjaśnię jak działa EF Core pod maską i pokażę, jak pisać wydajny kod zgodny z Clean Architecture.
🔍 IEnumerable vs IQueryable – o co naprawdę chodzi?
Na pierwszy rzut oka oba interfejsy wyglądają identycznie:
- oba wspierają LINQ
- oba działają z
foreach - oba kompilują się poprawnie
👉 Ale różnica jest fundamentalna: gdzie wykonywane jest zapytanie.
📌 IEnumerable – przetwarzanie w pamięci (LINQ to Objects)
var orders = repository.GetAllOrdersAsEnumerable()
.Where(o => o.TotalAmount > 1000)
.ToList();
Co się tutaj dzieje?
- EF Core wykonuje:
SELECT * FROM Orders
- dane trafiają do pamięci (RAM)
- dopiero potem działa
.Where()

❌ Problem
- pobierasz wszystkie rekordy
- filtrujesz je lokalnie
- marnujesz:
- RAM
- CPU
- sieć
👉 To klasyczny antywzorzec: Client-Side Evaluation
📌 IQueryable – przetwarzanie w bazie (LINQ to Entities)

var orders = repository.GetAllOrdersAsQueryable()
.Where(o => o.TotalAmount > 1000)
.ToList();
Co robi EF Core?
SELECT * FROM Orders WHERE TotalAmount > 1000
👉 Filtr odbywa się w bazie danych.
✅ Efekt
- mniej danych w sieci
- mniej RAM
- lepsza wydajność
- skalowalność
⚙️ Jak to działa pod maską?
Kluczowa różnica:
| Typ | Mechanizm |
IEnumerable | Delegaty (Func<T, bool>) |
IQueryable | Expression Trees |
🧠 Expression Trees – dlaczego to działa?

Expression<Func<Order, bool>> expr = o => o.TotalAmount > 1000;
EF Core:
- analizuje drzewo wyrażeń
- tłumaczy je na SQL
- wysyła do bazy
👉 To nie jest wykonywanie kodu — to opis zapytania
🔥 Najczęstsze pułapki (realne case’y produkcyjne)

❌ 1. Przedwczesne .ToList()
var orders = context.Orders.ToList()
.Where(o => o.TotalAmount > 1000);
👉 Zabijasz optymalizację SQL
❌ 2. .AsEnumerable() w złym miejscu
var orders = context.Orders
.AsEnumerable()
.Where(o => o.TotalAmount > 1000);
👉 Przełączasz się na LINQ to Objects
❌ 3. Zbyt skomplikowane metody
.Where(o => CustomBusinessLogic(o))
👉 EF Core nie przetłumaczy tego na SQL
👉 runtime exception
🧱 Problem architektoniczny – Leaky Abstraction

Skoro IQueryable jest takie dobre…
👉 dlaczego nie zwracać go zawsze z repozytorium?
❌ Problem
public IQueryable<Order> GetOrders()
- warstwa wyżej buduje zapytania
- zna szczegóły EF Core
- łamiesz hermetyzację
🔥 Konsekwencje
- tight coupling
- trudne debugowanie
- błędy runtime zamiast compile-time
✅ Rozwiązanie: Specification Pattern

Zamiast:
repository.GetOrders().Where(...)
robisz:
repository.ListAsync(new ExpensiveOrdersSpecification());
📌 Przykład
public class ExpensiveOrdersSpecification : Specification<Order>
{
public ExpensiveOrdersSpecification()
{
Query.Where(o => o.TotalAmount > 1000);
}
}
✅ Zalety
- brak wycieku
IQueryable - pełna kontrola zapytań
- zgodność z Clean Architecture
- reużywalność
📊 Checklist – jak pisać wydajny kod w EF Core
✅ Filtruj na IQueryable
✅ Używaj .Select() do projekcji
✅ Loguj SQL (LogTo)
✅ Unikaj .ToList() za wcześnie
✅ Nie używaj .AsEnumerable() bez potrzeby
✅ Testuj na dużych danych
⚡ Szybkie podsumowanie
IEnumerable= przetwarzanie w RAMIQueryable= przetwarzanie w bazie- kluczowy jest moment wykonania zapytania
- zła decyzja = ogromne problemy wydajnościowe
IQueryablerozwiązuje wydajność, ale wprowadza problem architektoniczny- rozwiązaniem jest Specification Pattern
🔗 Zobacz też
- C# Podstawy Programowania: Twój Pierwszy Krok w Świat Kodowania
- AI w .NET: Zostań Architektem Inteligentnych Aplikacji!
- C# Clean Architecture w Praktyce
- C# – Zbuduj Własnego Tetrisa! Kompletny Przewodnik
- 7 Dniowe Wyzwanie C# Tic Tac Toe
- C# Zbuduj Profesjonalny Portal Randkowy od Podstaw!
📢 Call To Action
Jeśli ten artykuł pomógł Ci zrozumieć różnicę między IEnumerable a IQueryable:
👉 zostaw komentarz – jakie problemy wydajnościowe spotkałeś w EF Core?
👉 udostępnij artykuł innym devom
Zobacz także — powiązane artykuły
👉 Tworzenie klas i obiektów w C# — kompletny przewodnik
👉LINQ w C# — przetwarzanie kolekcji bez pętli – zobacz w kursie LINQ w C# -czytelny kod, wydajne zapytania
👉 Typy wartościowe vs referencyjne w C# — jak działa pamięć – zobacz w kursie C# Podstawy Programowania: Twój Pierwszy Krok w Świat Kodowania
Dołącz do Listy VIP
I otrzymaj roadmapę Junior .NET Developer oraz najlepszą ofertę, gdy tylko ruszą zapisy!!!

