Regra prática: qualquer método "materializador" (ToList, Any, Count, foreach...) dispara o SQL. Tudo antes é apenas a definição da query — nada vai pro banco.
AspectoIEnumerable<T>IQueryable<T>
Onde filtraMemória (C#)Banco (SQL WHERE)
Carrega tudo?Sim — todos os registrosNão — só o necessário
LINQ providerLINQ to ObjectsLINQ to SQL (EF Core)
AsEnumerable()Muda o contexto — tudo depois roda em memória, não em SQL
// ❌ RUIM: carrega TUDO em memória, filtra no C# var bad = await db.Orders.ToListAsync(); // SELECT * FROM Orders (sem WHERE) var filtered = bad.Where(o => o.Total > 100); // filtra em C# — pode ser 1M registros // ✅ BOM: filtra no banco via SQL var good = await db.Orders .Where(o => o.Total > 100) // WHERE Total > 100 .ToListAsync(); // executa o SQL // ⚠️ AsEnumerable() muda o contexto para memória var mixed = db.Orders .Where(o => o.Status == "Open") // → SQL WHERE .AsEnumerable() // ← switch para memória .Where(o => o.Note.Contains("x"));// → C# (não SQL) // ⚠️ foreach também materializa foreach (var o in db.Orders.Where(o => o.Total > 100)) // SQL executa AQUI Console.WriteLine(o.Id);