LINQ Fundamentals

Master Language Integrated Query (LINQ) to write expressive, efficient queries over collections and data sources in C#

C#
LINQ
Data Queries
Functional Programming
20 min read

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 };

Bad vs Good Examples

Bad: 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);

Good: Single Enumeration

// Good - 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);

Bad: Inefficient Filtering

// Bad - filters, then filters again
var result = products
    .Where(p => p.IsActive)
    .Select(p => p.Name)
    .Where(name => name.StartsWith("A"));

Good: Combined Filtering

// Good - filter once
var result = products
    .Where(p => p.IsActive && p.Name.StartsWith("A"))
    .Select(p => p.Name);

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

  1. 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.
  2. 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.
  3. What's the difference between Select and SelectMany?

    • Select: One-to-one projection. SelectMany: Flattens nested collections (one-to-many), like SQL JOIN.
  4. 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.
  5. How does LINQ improve code readability?

    • Declarative syntax (what, not how), chainable operations, less boilerplate than loops, compile-time checking.
  6. 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.
  7. Can you modify a collection while enumerating it with LINQ?

    • No, throws InvalidOperationException. Must materialize first (.ToList()) or use different approach.