Skip to main content

C# LINQ Tutorial: 5 Practical Examples You'll Use Daily

Learn C# LINQ with 5 practical examples every developer uses daily. Master Where, Select, GroupBy & more with runnable code. Start writing cleaner C# today!

C# LINQ (Language Integrated Query) is one of the most powerful features in the .NET ecosystem, and learning it well will instantly make you a faster, cleaner C# developer. If you've ever written a tangled foreach loop just to filter a list, group some records, or pull a single field out of a collection, then this C# LINQ tutorial is for you. In this guide you'll learn LINQ through 5 practical examples that working developers in the USA, UK, Canada, and Australia use every single day.

By the end, you'll understand not just how to write a LINQ query in C#, but why it works the way it does — including deferred execution, the difference between method syntax and query syntax, and the common pitfalls that trip up beginners and intermediates alike.

What Is C# LINQ and Why Should You Use It?

LINQ is a set of extension methods (and a query syntax) that lets you query in-memory collections, databases, XML, and more using a single, consistent, readable API. Instead of writing imperative loops that describe how to get a result step by step, LINQ lets you write declarative code that describes what result you want.

Consider the difference. Here is the old, loop-based approach:

var activeUsers = new List<User>();
foreach (var user in users)
{
    if (user.IsActive)
    {
        activeUsers.Add(user);
    }
}

And here is the same logic with C# LINQ:

var activeUsers = users.Where(u => u.IsActive).ToList();

One line instead of eight. It reads almost like English, there's less room for bugs, and the intent is crystal clear. To use LINQ in any file, just add using System.Linq; at the top — it works on any type that implements IEnumerable<T>, which includes arrays, lists, dictionaries, and more.

For all examples below, assume we have this simple model and sample data:

public record Product(int Id, string Name, string Category, decimal Price, int Stock);

var products = new List<Product>
{
    new(1, "Laptop",   "Electronics", 1200m, 5),
    new(2, "Mouse",    "Electronics", 25m,   0),
    new(3, "Desk",     "Furniture",   300m,  12),
    new(4, "Chair",    "Furniture",   150m,  0),
    new(5, "Monitor",  "Electronics", 400m,  8),
    new(6, "Notebook", "Stationery",  3m,    200)
};

Example 1: Filtering Data with Where (the Most Common LINQ Query)

The Where method is the bread and butter of every LINQ query in C#. It takes a predicate — a function that returns true or false — and returns only the elements that match.

// Get all products that are in stock and cost less than $500
var affordableInStock = products
    .Where(p => p.Stock > 0 && p.Price < 500m)
    .ToList();

foreach (var p in affordableInStock)
    Console.WriteLine($"{p.Name} - ${p.Price}");

// Output:
// Desk - $300
// Monitor - $400
// Notebook - $3

Why it works this way: Where uses deferred execution. The filtering doesn't actually happen when you call Where — it happens when you enumerate the result (here, via ToList() or the foreach). This is a key LINQ concept that saves performance because the query only runs when you genuinely need the data.

Common pitfall: Forgetting to materialize the query. If you store a Where result and the underlying collection changes before you enumerate it, you'll get the new data, not a snapshot. Call .ToList() or .ToArray() when you need a stable copy.

Example 2: Transforming Data with Select (Projection)

Often you don't want whole objects — you want just one field, or a reshaped object. That's what Select (projection) is for. This is the second-most-used LINQ method after Where.

// Extract just the product names
var names = products.Select(p => p.Name).ToList();

// Project into a new anonymous shape for an API response
var summary = products
    .Where(p => p.Stock > 0)
    .Select(p => new
    {
        p.Name,
        PriceWithTax = Math.Round(p.Price * 1.08m, 2)
    })
    .ToList();

foreach (var item in summary)
    Console.WriteLine($"{item.Name}: ${item.PriceWithTax}");

Why use Select? Projecting to a smaller shape (a DTO or anonymous type) is a best practice when sending data over the network or to a UI — you avoid leaking internal fields and reduce payload size. When using LINQ with Entity Framework, Select also tells the database to fetch only the columns you need, which is a major performance win.

Tip: If your projection returns a collection per element (e.g., each order has many items), use SelectMany instead of Select to flatten the nested collections into one sequence.

Example 3: Sorting with OrderBy and ThenBy

Sorting is a daily task, and LINQ makes multi-level sorting trivial with OrderBy, OrderByDescending, and ThenBy.

// Sort by category alphabetically, then by price high-to-low
var sorted = products
    .OrderBy(p => p.Category)
    .ThenByDescending(p => p.Price)
    .ToList();

foreach (var p in sorted)
    Console.WriteLine($"{p.Category,-12} {p.Name,-10} ${p.Price}");

Why ThenBy matters: A common beginner mistake is chaining OrderBy(...).OrderBy(...), which actually re-sorts the entire sequence and discards the first sort. Always use ThenBy / ThenByDescending for secondary sort keys. The first OrderBy establishes the primary order; each ThenBy breaks ties.

Example 4: Grouping Data with GroupBy

When you need to bucket items — say, totaling sales per category or counting users per country — GroupBy is your friend. This is where LINQ really shines compared to manual loops with dictionaries.

// Group products by category and calculate stats for each group
var byCategory = products
    .GroupBy(p => p.Category)
    .Select(g => new
    {
        Category = g.Key,
        Count = g.Count(),
        TotalValue = g.Sum(p => p.Price * p.Stock),
        AveragePrice = g.Average(p => p.Price)
    })
    .OrderByDescending(x => x.TotalValue)
    .ToList();

foreach (var c in byCategory)
    Console.WriteLine($"{c.Category}: {c.Count} items, inventory value ${c.TotalValue}");

// Output:
// Electronics: 3 items, inventory value $9200
// Furniture: 2 items, inventory value $3600
// Stationery: 1 items, inventory value $600

Why this is powerful: Each group is an IGrouping<TKey, TElement>, which exposes the Key and is itself enumerable. That means you can run aggregate functions — Count, Sum, Average, Min, Max — directly on each group. Doing this manually would require a dictionary, conditional initialization, and running totals: easily 15+ lines that's harder to read and easier to get wrong.

Example 5: Getting Single Results with First, Single, and Any

Sometimes you need exactly one element or a yes/no answer. These LINQ methods are essential for everyday logic, but they have important behavioral differences you must understand.

// Safely get the first match, or null if none exists
Product? firstCheap = products.FirstOrDefault(p => p.Price < 50m);

// Throws if zero OR more than one match - use when exactly one is expected
Product laptop = products.Single(p => p.Name == "Laptop");

// Boolean checks - fast and readable
bool hasOutOfStock = products.Any(p => p.Stock == 0);   // true
bool allHavePrices = products.All(p => p.Price > 0);    // true

// Aggregate helpers
decimal mostExpensive = products.Max(p => p.Price);     // 1200
int totalStock = products.Sum(p => p.Stock);           // 225

Why choose the right method: This is one of the most important LINQ best practices.

  • First() / Single() throw an exception if nothing matches — use them only when an empty result is genuinely a bug.
  • FirstOrDefault() / SingleOrDefault() return default (null for reference types) instead — the safer choice for optional lookups.
  • Single additionally throws if there's more than one match, which is perfect for enforcing uniqueness (like looking up by a primary key).
  • Any() is far more efficient than Count() > 0 for checking existence, because it stops at the first match instead of iterating the entire collection.

Method Syntax vs Query Syntax in LINQ

Everything above used method syntax (the fluent .Where().Select() chain). LINQ also offers query syntax, which looks more like SQL:

var query =
    from p in products
    where p.Stock > 0
    orderby p.Price descending
    select p.Name;

Both compile to the exact same thing. Method syntax is more common in real-world C# code and supports every operator, while query syntax can be more readable for complex joins and groupings. Use whichever your team prefers — most developers favor method syntax for its consistency.

LINQ Best Practices and Common Pitfalls

  • Avoid multiple enumeration. If you'll use a query result more than once, call .ToList() once and reuse it. Re-enumerating a deferred query runs the whole pipeline again.
  • Filter before you project. Put Where before Select so you do less transformation work and, in Entity Framework, generate tighter SQL.
  • Be careful with LINQ-to-Entities. Not every C# method can be translated to SQL. Calling unsupported methods triggers runtime errors or silent client-side evaluation — a major performance trap.
  • Prefer Any() over Count() > 0 for existence checks.
  • Use FirstOrDefault over First unless an empty sequence should be treated as an error.

Conclusion: Master C# LINQ to Write Cleaner Code Faster

You've now seen the 5 LINQ operations you'll reach for almost every day: filtering with Where, transforming with Select, sorting with OrderBy/ThenBy, grouping with GroupBy, and fetching single results with First/Single/Any. Together, these cover the vast majority of real-world data manipulation in C# applications.

Key takeaways from this C# LINQ tutorial:

  • LINQ replaces verbose loops with readable, declarative queries.
  • Deferred execution means queries run only when enumerated — materialize with ToList() when needed.
  • Choose the safe variants (FirstOrDefault, Any) to avoid exceptions and improve performance.
  • Method syntax and query syntax compile to the same code — pick what reads best.

The best way to learn LINQ is to use it. Take a foreach loop in your current project and try rewriting it as a LINQ query in C# today — you'll be surprised how much cleaner your code becomes. Happy coding!

About csharp-coder.com
Your go-to resource for C#, .NET, and modern software development. Follow along for daily tutorials, tips, and real-world examples.

Comments

Popular posts from this blog

Angular 14 CRUD Operation with Web API .Net 6.0

How to Perform CRUD Operation Using Angular 14 In this article, we will learn the angular crud (create, read, update, delete) tutorial with ASP.NET Core 6 web API. We will use the SQL Server database and responsive user interface for our Web app, we will use the Bootstrap 5. Let's start step by step. Step 1 - Create Database and Web API First we need to create Employee database in SQL Server and web API to communicate with database. so you can use my previous article CRUD operations in web API using net 6.0 to create web API step by step. As you can see, after creating all the required API and database, our API creation part is completed. Now we have to do the angular part like installing angular CLI, creating angular 14 project, command for building and running angular application...etc. Step 2 - Install Angular CLI Now we have to install angular CLI into our system. If you have already installed angular CLI into your system then skip this step.  To install angular CLI ope...

Angular 14 : 404 error during refresh page after deployment

In this article, We will learn how to solve 404 file or directory not found angular error in production.  Refresh browser angular 404 file or directory not found error You have built an Angular app and created a production build with ng build --prod You deploy it to a production server. Everything works fine until you refresh the page. The app throws The requested URL was not found on this server message (Status code 404 not found). It appears that angular routing not working on the production server when you refresh the page. The error appears on the following scenarios When you type the URL directly in the address bar. When you refresh the page The error appears on all the pages except the root page.   Reason for the requested URL was not found on this server error In a Multi-page web application, every time the application needs to display a page it has to send a request to the web server. You can do that by either typing the URL in the address bar, clicking on the Me...

Send an Email via SMTP with MailKit Using .NET 6

How to Send an Email in .NET Core This tutorial show you how to send an email in .NET 6.0 using the MailKit email client library. Install MailKit via NuGet Visual Studio Package Manager Console: Install-Package MailKit How to Send an HTML Email in .NET 6.0 This code sends a simple HTML email using the Gmail SMTP service. There are instructions further below on how to use a few other popular SMTP providers - Gmail, Hotmail, Office 365. // create email message var email = new MimeMessage(); email.From.Add(MailboxAddress.Parse("from_address@example.com")); email.To.Add(MailboxAddress.Parse("to_address@example.com")); email.Subject = "Email Subject"; email.Body = new TextPart(TextFormat.Html) { Text = "<h1>Test HTML Message Body</h1>" }; // send email using var smtp = new SmtpClient(); smtp.Connect("smtp.gmail.com", 587, SecureSocketOptions.StartTls); smtp.Authenticate("[Username]", "[Password]"); smtp.Se...