A Guide to LINQ in C#: Querying Any Collection
An introduction to LINQ (Language-Integrated Query) in C#. Learn how to use its powerful and expressive syntax to query in-memory collections, databases, and other data sources.
One of the most powerful and beloved features of C# is LINQ (Language-Integrated Query). LINQ is a set of technologies that integrates query capabilities directly into the C# language. It provides a consistent, expressive, and powerful way to query data from a variety of sources, including in-memory collections, databases, XML documents, and more.
If you're a C# developer, mastering LINQ is not optional; it's an essential skill for writing clean and efficient data manipulation code.
The Problem Before LINQ
Before LINQ, if you wanted to query data from a collection, you had to write foreach
loops with if
statements. If you wanted to query a database, you had to write SQL strings. Each data source had its own separate way of being queried. LINQ provides a unified model for querying any data source that implements the IEnumerable<T>
interface.
The Two Syntaxes of LINQ
LINQ has two different syntaxes that you can use, which are compiled to the exact same code.
- Query Syntax (or Declaration Syntax): This syntax looks very similar to SQL.
- Method Syntax (or Fluent Syntax): This syntax uses a chain of extension methods.
While you can use either, the C# community has largely standardized on using Method Syntax for its fluency and composability.
LINQ in Action: Using Method Syntax
Let's look at some of the most common LINQ operators using a simple collection of Product
objects.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}
var products = new List<Product>
{
new Product { Id = 1, Name = "Laptop", Category = "Electronics", Price = 1200.00m },
new Product { Id = 2, Name = "Keyboard", Category = "Electronics", Price = 80.00m },
new Product { Id = 3, Name = "T-Shirt", Category = "Apparel", Price = 25.00m },
new Product { Id = 4, Name = "Jeans", Category = "Apparel", Price = 60.00m },
};
Filtering with Where
The Where
method filters a sequence based on a predicate (a function that returns a boolean).
// Find all products in the 'Electronics' category
var electronics = products.Where(p => p.Category == "Electronics");
Projecting with Select
The Select
method projects each element of a sequence into a new form. It's used to transform the data.
// Get just the names of all the products
var productNames = products.Select(p => p.Name);
Chaining Methods
The real power of LINQ comes from chaining these methods together.
// Get the names of all apparel products that cost more than $50
var expensiveApparelNames = products
.Where(p => p.Category == "Apparel" && p.Price > 50.00m)
.Select(p => p.Name);
This is incredibly readable. It clearly states the intent of the query.
Other Common LINQ Methods
OrderBy
/OrderByDescending
: Sorts the elements of a sequence.var sortedByPrice = products.OrderByDescending(p => p.Price);
First
/FirstOrDefault
: Returns the first element of a sequence.First
will throw an exception if the sequence is empty, whileFirstOrDefault
will return the default value (null
for reference types).var firstProduct = products.First(p => p.Category == "Electronics");
Single
/SingleOrDefault
: Similar toFirst
, but it throws an exception if there is more than one element that matches the condition.Any
: Checks if any element in the sequence satisfies a condition.bool hasCheapProducts = products.Any(p => p.Price < 10.00m);
All
: Checks if all elements in the sequence satisfy a condition.Count
: Counts the number of elements in a sequence.Sum
/Average
/Max
/Min
: Perform aggregate calculations on a sequence.decimal totalValue = products.Sum(p => p.Price);
Deferred Execution
One of the most important concepts to understand about LINQ is deferred execution. Most LINQ queries are not executed when they are defined. They are only executed when the results are actually enumerated.
var electronicsQuery = products.Where(p => p.Category == "Electronics"); // Query is NOT run here
// The query is executed here, when we loop through it.
foreach (var product in electronicsQuery)
{
Console.WriteLine(product.Name);
}
Methods that force immediate execution include ToList()
, ToArray()
, Count()
, First()
, and the aggregate methods.
LINQ to SQL
The beauty of LINQ is that the same syntax works for other data sources. When you use LINQ with a database via an ORM like Entity Framework, your LINQ query is translated into SQL and executed on the database server. This allows you to write strongly-typed, compile-checked queries for your database.
Conclusion
LINQ is a transformative feature of C#. It provides a single, unified way to work with data that is both powerful and highly readable. By replacing complex loops and conditional logic with a fluent chain of query operators, LINQ allows you to express your intent clearly and concisely, leading to code that is easier to write, read, and maintain.