Skip to content

LINQ in C# — Complete Guide with Examples

DodaTech Updated 2026-06-20 8 min read

In this tutorial, you'll learn about LINQ in C#. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Language-Integrated Query (LINQ) is a C# feature that lets you query collections, databases, XML, and more using a consistent syntax directly inside the language, without writing separate SQL or loops.

What You'll Learn

You'll understand LINQ's core concepts — query syntax, method syntax, deferred execution, and common operators like Where, Select, GroupBy, and Join — with practical examples you can run today.

Why LINQ Matters

LINQ replaces dozens of lines of imperative loop code with one expressive query. At DodaTech, our Durga Antivirus Pro uses LINQ to filter threat signatures, group scan results, and join log data from multiple sources — reducing code by over 60%. Mastering LINQ makes you a more productive C# developer.

Real-World Use

A security analyst queries millions of log entries to find suspicious IP addresses. With LINQ, you filter, group, and sort those entries in a few lines instead of nested foreach loops.

LINQ Learning Path

flowchart LR
  A["C# Basics"] --> B["Collections & Iteration"]
  B --> C["LINQ Fundamentals"]
  C --> D["LINQ Operators"]
  D --> E["Entity Framework Core"]
  E --> F["Real-World Data Apps"]
  C:::current
  classDef current fill:#f90,color:#fff,stroke:#333,stroke-width:2px
â„šī¸ Info

Prerequisites: Basic C# knowledge — variables, methods, and generic collections like List<T>. Install .NET SDK 6+ from dotnet.microsoft.com.

What Is LINQ?

LINQ stands for Language-Integrated Query. It was introduced in C# 3.0 and .NET Framework 3.5. Before LINQ, querying data meant:

  • Writing foreach loops to filter lists
  • Writing raw SQL strings for databases
  • Manually joining data from different sources

LINQ unifies all of these under one syntax. You write the same query against an in-memory list, an SQL database, or an XML document.

Two Syntaxes

Syntax Style Example
Query Syntax SQL-like declarative from p in products where p.Price > 100 select p
Method Syntax Fluent method chaining products.Where(p => p.Price > 100)

Both produce the same result. Method syntax is more common in professional codebases.

LINQ Operators Cheat Sheet

Operator Purpose Method Syntax
Where Filter items .Where(x => condition)
Select Transform items .Select(x => new { x.Name })
OrderBy Sort ascending .OrderBy(x => x.Price)
GroupBy Group items .GroupBy(x => x.Category)
Join Combine two sources .Join(inner, outerKey, innerKey, result)
Aggregate Accumulate values .Aggregate(0, (acc, x) => acc + x)
Any / All Check conditions .Any(x => x.IsActive)

Code Examples

Example 1: Filtering and Transforming a List

using System;
using System.Collections.Generic;
using System.Linq;

var numbers = new List<int> { 10, 23, 35, 42, 57, 68, 71 };

// Method syntax: filter even numbers, then double them
var result = numbers
    .Where(n => n % 2 == 0)
    .Select(n => n * 2);

Console.WriteLine(string.Join(", ", result));

Expected output:

20, 84, 136

What's happening:

  • Where(n => n % 2 == 0) keeps only numbers where the remainder when divided by 2 is 0 — the even numbers (10, 42, 68)
  • Select(n => n * 2) multiplies each remaining number by 2
  • string.Join produces a comma-separated string

Example 2: Grouping and Aggregating

using System;
using System.Collections.Generic;
using System.Linq;

var products = new List<(string Name, string Category, decimal Price)>
{
    ("Laptop", "Electronics", 1200m),
    ("Phone", "Electronics", 800m),
    ("Shirt", "Clothing", 25m),
    ("Jeans", "Clothing", 55m),
    ("Tablet", "Electronics", 350m)
};

var grouped = products
    .GroupBy(p => p.Category)
    .Select(g => new
    {
        Category = g.Key,
        Count = g.Count(),
        AveragePrice = g.Average(p => p.Price)
    });

foreach (var item in grouped)
{
    Console.WriteLine($"{item.Category}: {item.Count} items, avg ${item.AveragePrice:F2}");
}

Expected output:

Clothing: 2 items, avg $40.00
Electronics: 3 items, avg $783.33

What's happening:

  • GroupBy(p => p.Category) splits the list into groups — one per unique category value
  • Each group has a Key (the category name) and is itself a collection of products
  • Count() and Average() are aggregate functions applied per group

Example 3: Joining Two Data Sources

using System;
using System.Collections.Generic;
using System.Linq;

var customers = new List<(int Id, string Name)>
{
    (1, "Alice"),
    (2, "Bob"),
    (3, "Charlie")
};

var orders = new List<(int OrderId, int CustomerId, decimal Amount)>
{
    (101, 1, 250m),
    (102, 2, 180m),
    (103, 1, 90m),
    (104, 3, 450m)
};

var query = customers.Join(
    orders,
    c => c.Id,
    o => o.CustomerId,
    (c, o) => new { c.Name, o.OrderId, o.Amount }
);

foreach (var item in query)
{
    Console.WriteLine($"{item.Name} - Order #{item.OrderId}: ${item.Amount}");
}

Expected output:

Alice - Order #101: $250
Bob - Order #102: $180
Alice - Order #103: $90
Charlie - Order #104: $450

What's happening:

  • Join connects two lists using matching keys — like a SQL INNER JOIN
  • The first lambda c => c.Id specifies the key from the outer (customer) list
  • The second lambda o => o.CustomerId specifies the key from the inner (order) list
  • The result lambda (c, o) => ... creates the output shape

Example 4: Security Log Analysis with LINQ (Unique Content)

using System;
using System.Collections.Generic;
using System.Linq;

var logEntries = new List<(string IP, DateTime Time, string Action, int Severity)>
{
    ("192.168.1.10", DateTime.Now.AddHours(-2), "LOGIN_FAILED", 5),
    ("10.0.0.5", DateTime.Now.AddHours(-1), "FILE_ACCESS", 2),
    ("192.168.1.10", DateTime.Now.AddHours(-1), "LOGIN_FAILED", 5),
    ("192.168.1.10", DateTime.Now.AddMinutes(-30), "LOGIN_FAILED", 5),
    ("10.0.0.5", DateTime.Now.AddMinutes(-20), "FILE_DOWNLOAD", 3),
    ("172.16.0.8", DateTime.Now.AddMinutes(-10), "ADMIN_LOGIN", 1),
};

// Find IPs with 3+ failed login attempts (brute-force detection)
var bruteForceSuspects = logEntries
    .Where(e => e.Action == "LOGIN_FAILED")
    .GroupBy(e => e.IP)
    .Where(g => g.Count() >= 3)
    .Select(g => new { IP = g.Key, Attempts = g.Count(), LastAttempt = g.Max(e => e.Time) });

foreach (var suspect in bruteForceSuspects)
{
    Console.WriteLine($"ALERT: {suspect.IP} - {suspect.Attempts} failed logins, last at {suspect.LastAttempt:t}");
}

Expected output:

ALERT: 192.168.1.10 - 3 failed logins, last at 3:00 PM

This pattern is used inside Durga Antivirus Pro to detect brute-force attacks in real time.

Deferred Execution

LINQ queries do not execute when you define them. They execute when you iterate them (foreach, .ToList(), .Count(), etc.). This is called deferred execution.

var query = numbers.Where(n => n > 10); // Nothing happens
numbers.Add(99); // Add after query definition
Console.WriteLine(query.Count()); // Output: 5 — includes the 99!

Common Errors

  1. Multiple enumeration: Iterating the same IEnumerable<T> multiple times executes the query each time. Call .ToList() or .ToArray() to cache results.

  2. Null reference in Where predicate: If your collection contains null items, x.Property in the lambda throws NullReferenceException. Use x?.Property or filter nulls first.

  3. LINQ to Entities vs LINQ to Objects confusion: Some methods like Last() work in LINQ to Objects but are not supported in Entity Framework (LINQ to Entities). Use OrderByDescending().First() instead.

  4. Forgetting Select projects the type: If you don't call Select, you get the original type. Use Select to shape the result.

  5. Using Count() vs Any(): Any() is O(1) for collections that support it; Count() enumerates the entire sequence. Use Any() to check if items exist.

  6. Modifying collection during iteration: Adding or removing items from the source collection while iterating a LINQ query throws InvalidOperationException.

  7. Culture-sensitive string comparisons: Where(x => x.Name == "hello") uses the current culture. For case-insensitive ordinal (like comparing IDs), use StringComparer.OrdinalIgnoreCase.

Practice Questions

  1. What is the difference between Where and Select in LINQ?
  2. What does deferred execution mean?
  3. How do you prevent a LINQ query from executing multiple times?
  4. Which LINQ method would you use to find the first element matching a condition?
  5. What does GroupBy return?

Answers:

  1. Where filters items based on a condition; Select transforms each item into a new shape.
  2. The query is not executed when defined — only when iterated, listed, or aggregated.
  3. Call .ToList() or .ToArray() to materialize the results into an in-memory collection.
  4. FirstOrDefault(predicate) — returns the first match or default (null for reference types).
  5. GroupBy returns a sequence of IGrouping<TKey, TElement> objects, each with a Key property and the grouped items.

Challenge

Write a LINQ query that takes a list of strings, groups them by their first character (uppercase), counts how many words start with each letter, and orders the results by count descending.

Real-World Task

You have a CSV log file of HTTP requests with columns: Timestamp, IP, StatusCode, BytesSent. Use LINQ to:

  • Count requests per IP
  • Find the top 10 IPs by total bytes transferred
  • Identify IPs with more than 100 4xx or 5xx status codes (potential attackers)

What is LINQ in C#?

LINQ (Language-Integrated Query) is a C# feature that provides SQL-like query capabilities directly in the language, enabling you to filter, sort, group, and transform data from collections, databases, XML, and other sources using a consistent syntax.

FAQ

What is the difference between LINQ query syntax and method syntax?

Query syntax uses SQL-like keywords (from, where, select) while method syntax uses extension methods (Where(), Select()). Both compile to the same IL code — choose whichever is more readable for your scenario.

Can I use LINQ with databases?

Yes. LINQ to Entities (via Entity Framework) translates LINQ queries into SQL and executes them on the database. You get compile-time checking and IntelliSense for database queries.

Is LINQ slower than manual loops?

LINQ adds a small overhead, but for most scenarios the difference is negligible. The productivity gain, readability, and reduced bug surface far outweigh the micro-performance cost.

Try It Yourself

▶ Try It YourselfEdit the code and click Run

What's Next

Entity Framework Core — ORM and Database Access
C# Programming Language
.NET CLI — Command Line Complete Guide

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro