Overview
LINQ revolutionized C# development by bringing functional programming concepts to .NET. Understanding LINQ deeply is essential for writing clean, efficient code and is a frequent interview topic for mid-level and above positions.
Core Concepts
Query Syntax vs Method Syntax
var products = GetProducts();
// Query syntax - more readable for complex queries
var queryResult = from p in products
where p.Price > 50
orderby p.Name
select new { p.Name, p.Price };
// Method syntax - more flexible and composable
var methodResult = products
.Where(p => p.Price > 50)
.OrderBy(p => p.Name)
.Select(p => new { p.Name, p.Price });
Deferred vs Immediate Execution
var numbers = new List<int> { 1, 2, 3 };
// Deferred execution - query stored, not executed
var query = numbers.Where(n => n > 1);
numbers.Add(4); // Affects query results!
foreach (var n in query) // Executes here
Console.WriteLine(n); // Output: 2, 3, 4
// Immediate execution with .ToList(), .ToArray(), .Count()
var list = numbers.Where(n => n > 1).ToList(); // Executes immediately
numbers.Add(5); // Doesn't affect list
Common LINQ Operations
var products = GetProducts();
// Filtering
var expensive = products.Where(p => p.Price > 100);
// Projection
var names = products.Select(p => p.Name);
var summary = products.Select(p => new { p.Name, Tax = p.Price * 0.1m });
// Ordering
var sorted = products.OrderBy(p => p.Price).ThenBy(p => p.Name);
// Grouping
var byCategory = products.GroupBy(p => p.Category);
foreach (var group in byCategory)
{
Console.WriteLine($"Category: {group.Key}");
foreach (var product in group)
Console.WriteLine($" {product.Name}");
}
// Aggregation
var total = products.Sum(p => p.Price);
var average = products.Average(p => p.Price);
var maxPrice = products.Max(p => p.Price);
Joining Data
var customers = GetCustomers();
var orders = GetOrders();
// Inner join
var customerOrders = from c in customers
join o in orders on c.Id equals o.CustomerId
select new { c.Name, o.OrderDate, o.Total };
// Left join (group join)
var customersWithOrders = from c in customers
join o in orders on c.Id equals o.CustomerId into orderGroup
select new { Customer = c, Orders = orderGroup };
When to Use vs. When to Avoid
| Scenario | Use It? | Why | |----------|---------|-----| | Filtering/sorting in-memory collections | ✅ Yes | Clean, readable syntax | | Database queries with EF Core | ✅ Yes | Translates to efficient SQL | | Simple one-time filter | ⚠️ Depends | Loop may be clearer for trivial cases | | Performance-critical tight loops | ❌ No | Slight overhead vs hand-written loops | | Operations not supported by IQueryable | ❌ No | Forces client-side evaluation |
Common Patterns
Filter-Transform-Sort Pattern
var result = products
.Where(p => p.IsActive && p.Price > 10)
.OrderBy(p => p.Name)
.Select(p => new { p.Name, p.Price });
GroupBy Pattern
var byCategory = products
.GroupBy(p => p.Category)
.Select(g => new { Category = g.Key, Count = g.Count(), Total = g.Sum(p => p.Price) });
Common Mistakes
Mistake: Multiple Enumerations
// ❌ Bad - enumerates collection 3 times
var items = GetExpensiveItems();
var count = items.Count();
var sum = items.Sum(x => x.Price);
var avg = items.Average(x => x.Price);
// ✅ Better - enumerates once
var items = GetExpensiveItems().ToList(); // Materialize once
var count = items.Count;
var sum = items.Sum(x => x.Price);
var avg = items.Average(x => x.Price);
Why: Each LINQ method call on IEnumerable re-enumerates the source. For database queries, this means multiple round-trips.
Mistake: Filtering After Select
// ❌ Bad - projects all, then filters strings
var result = products
.Where(p => p.IsActive)
.Select(p => p.Name)
.Where(name => name.StartsWith("A"));
// ✅ Better - filter once before projection
var result = products
.Where(p => p.IsActive && p.Name.StartsWith("A"))
.Select(p => p.Name);
Why: Filter early to reduce the data being processed in subsequent operations.
Practical Example
Scenario: Build a product search with filtering, sorting, and paging.
public List<ProductDto> SearchProducts(
string? searchTerm,
decimal? minPrice,
string? sortBy,
int page,
int pageSize)
{
var query = products.AsQueryable();
// Apply filters conditionally
if (!string.IsNullOrWhiteSpace(searchTerm))
query = query.Where(p => p.Name.Contains(searchTerm));
if (minPrice.HasValue)
query = query.Where(p => p.Price >= minPrice.Value);
// Apply sorting
query = sortBy?.ToLower() switch
{
"price" => query.OrderBy(p => p.Price),
"name" => query.OrderBy(p => p.Name),
_ => query.OrderBy(p => p.Id)
};
// Apply paging and project
return query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(p => new ProductDto { Name = p.Name, Price = p.Price })
.ToList();
}
Interview Tips
Tip 1: Always mention deferred execution when discussing LINQ. It's a common "gotcha" question.
Tip 2: Know when to use
.ToList()or.ToArray()to materialize results, especially before multiple enumerations.
Tip 3: Understand that LINQ to Entities (EF Core) translates to SQL - some operations force client-side evaluation.
Common Interview Questions
-
What's the difference between IEnumerable and IQueryable?
IEnumerable<T>: In-memory queries, client-side execution.IQueryable<T>: Expression tree-based, allows provider (like EF) to translate to SQL.
-
Explain deferred execution in LINQ.
- Query doesn't execute when defined, only when enumerated (foreach, ToList, Count, etc.). Allows query composition but can cause unexpected behavior if source changes.
-
What's the difference between Select and SelectMany?
Select: One-to-one projection.SelectMany: Flattens nested collections (one-to-many), like SQLJOIN.
-
When should you use ToList() or ToArray()?
- When you need to enumerate multiple times, when you want a snapshot of current data, or when you need indexed access.
-
How does LINQ improve code readability?
- Declarative syntax (what, not how), chainable operations, less boilerplate than loops, compile-time checking.
-
What are the performance implications of LINQ?
- Slight overhead vs hand-written loops, but negligible in most cases. Real issues: multiple enumerations, inefficient queries, client-side evaluation in EF.
-
Can you modify a collection while enumerating it with LINQ?
- No, throws
InvalidOperationException. Must materialize first (.ToList()) or use different approach.
- No, throws
Related Topics
- Entity Framework Core: Uses LINQ with IQueryable for database queries
- C# Collections: Understanding List, Array, Dictionary enhances LINQ usage
- Lambda Expressions: Foundation for LINQ method syntax
- Async/Await: Combine with LINQ using ToListAsync, FirstOrDefaultAsync