LINQ in C# â Complete Guide with Examples
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
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
foreachloops 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 2string.Joinproduces 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()andAverage()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:
Joinconnects two lists using matching keys â like a SQLINNER JOIN- The first lambda
c => c.Idspecifies the key from the outer (customer) list - The second lambda
o => o.CustomerIdspecifies 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
Multiple enumeration: Iterating the same
IEnumerable<T>multiple times executes the query each time. Call.ToList()or.ToArray()to cache results.Null reference in Where predicate: If your collection contains null items,
x.Propertyin the lambda throwsNullReferenceException. Usex?.Propertyor filter nulls first.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). UseOrderByDescending().First()instead.Forgetting
Selectprojects the type: If you don't callSelect, you get the original type. UseSelectto shape the result.Using
Count()vsAny():Any()is O(1) for collections that support it;Count()enumerates the entire sequence. UseAny()to check if items exist.Modifying collection during iteration: Adding or removing items from the source collection while iterating a LINQ query throws
InvalidOperationException.Culture-sensitive string comparisons:
Where(x => x.Name == "hello")uses the current culture. For case-insensitive ordinal (like comparing IDs), useStringComparer.OrdinalIgnoreCase.
Practice Questions
- What is the difference between
WhereandSelectin LINQ? - What does deferred execution mean?
- How do you prevent a LINQ query from executing multiple times?
- Which LINQ method would you use to find the first element matching a condition?
- What does
GroupByreturn?
Answers:
Wherefilters items based on a condition;Selecttransforms each item into a new shape.- The query is not executed when defined â only when iterated, listed, or aggregated.
- Call
.ToList()or.ToArray()to materialize the results into an in-memory collection. FirstOrDefault(predicate)â returns the first match or default (null for reference types).GroupByreturns a sequence ofIGrouping<TKey, TElement>objects, each with aKeyproperty 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
4xxor5xxstatus codes (potential attackers)
Featured Snippet
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
Try It Yourself
What's Next
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro