C# LINQ Query Syntax
Introduction
LINQ (Language Integrated Query) is a powerful feature in C# that allows you to query and manipulate data from different data sources using a consistent syntax. LINQ provides two ways to write queries: Query Syntax and Method Syntax. In this tutorial, we'll focus on LINQ Query Syntax, which resembles SQL and is often more readable for complex queries.
Query Syntax (also called "query expression syntax") uses SQL-like clauses such as from
, where
, and select
to form queries against collections. If you're familiar with SQL, you'll find LINQ Query Syntax intuitive and familiar.
Basic Query Syntax Structure
A LINQ query using Query Syntax typically follows this structure:
var result = from [variable] in [datasource]
[optional clauses]
select [expression];
Let's break down the components:
from [variable] in [datasource]
- Specifies the data source and a range variable.[optional clauses]
- Can include filtering (where
), sorting (orderby
), grouping (group by
), etc.select [expression]
- Defines what data to retrieve.
Simple LINQ Query Examples
Example 1: Filtering a Collection
Let's start with a simple example of filtering a list of numbers:
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Data source
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// LINQ query to find even numbers
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
// Display results
Console.WriteLine("Even numbers:");
foreach (var num in evenNumbers)
{
Console.Write($"{num} ");
}
// Output: Even numbers: 2 4 6 8 10
}
}
In this example, we:
- Define a data source (a list of integers)
- Write a query to filter even numbers using the
where
clause - Retrieve the results using the
select
clause - Iterate through the results to display them
Example 2: Transforming Data
We can also transform our data using LINQ queries:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var squaredNumbers = from num in numbers
select num * num;
Console.WriteLine("Squared numbers:");
foreach (var num in squaredNumbers)
{
Console.Write($"{num} ");
}
// Output: Squared numbers: 1 4 9 16 25
Here, we transform each number in the collection by squaring it.
Advanced LINQ Query Syntax Features
Sorting with orderby
You can sort query results using the orderby
clause:
List<string> fruits = new List<string> { "apple", "banana", "cherry", "date", "elderberry" };
// Sorting in ascending order (default)
var sortedFruits = from fruit in fruits
orderby fruit
select fruit;
// Sorting in descending order
var reverseSortedFruits = from fruit in fruits
orderby fruit descending
select fruit;
Console.WriteLine("Sorted fruits (ascending):");
foreach (var fruit in sortedFruits)
{
Console.WriteLine(fruit);
}
// Output:
// apple
// banana
// cherry
// date
// elderberry
Console.WriteLine("\nSorted fruits (descending):");
foreach (var fruit in reverseSortedFruits)
{
Console.WriteLine(fruit);
}
// Output:
// elderberry
// date
// cherry
// banana
// apple
Multiple Sorting Criteria
You can sort by multiple criteria:
var students = new List<Student>
{
new Student { Name = "Alice", Age = 22, Grade = 85 },
new Student { Name = "Bob", Age = 21, Grade = 85 },
new Student { Name = "Charlie", Age = 22, Grade = 90 },
new Student { Name = "Dave", Age = 20, Grade = 90 }
};
var sortedStudents = from student in students
orderby student.Grade descending, student.Age ascending
select student;
Console.WriteLine("Students sorted by grade (descending) then age (ascending):");
foreach (var student in sortedStudents)
{
Console.WriteLine($"Name: {student.Name}, Age: {student.Age}, Grade: {student.Grade}");
}
// Output:
// Name: Dave, Age: 20, Grade: 90
// Name: Charlie, Age: 22, Grade: 90
// Name: Bob, Age: 21, Grade: 85
// Name: Alice, Age: 22, Grade: 85
In this example, students are first sorted by grade in descending order, then by age in ascending order.
Grouping with group by
LINQ allows you to group data similar to SQL's GROUP BY:
var students = new List<Student>
{
new Student { Name = "Alice", Year = 1 },
new Student { Name = "Bob", Year = 2 },
new Student { Name = "Charlie", Year = 1 },
new Student { Name = "Dave", Year = 3 },
new Student { Name = "Eve", Year = 2 }
};
var studentGroups = from student in students
group student by student.Year into yearGroup
orderby yearGroup.Key
select new { Year = yearGroup.Key, Students = yearGroup };
foreach (var group in studentGroups)
{
Console.WriteLine($"Year {group.Year}:");
foreach (var student in group.Students)
{
Console.WriteLine($" {student.Name}");
}
}
/* Output:
Year 1:
Alice
Charlie
Year 2:
Bob
Eve
Year 3:
Dave
*/
Joining Collections
LINQ Query Syntax provides a way to join multiple collections, similar to SQL JOIN:
var students = new List<Student>
{
new Student { ID = 1, Name = "Alice" },
new Student { ID = 2, Name = "Bob" },
new Student { ID = 3, Name = "Charlie" }
};
var courses = new List<Course>
{
new Course { StudentID = 1, CourseName = "Math" },
new Course { StudentID = 1, CourseName = "Science" },
new Course { StudentID = 2, CourseName = "History" },
new Course { StudentID = 3, CourseName = "English" },
new Course { StudentID = 3, CourseName = "Physics" }
};
var studentCourses = from student in students
join course in courses
on student.ID equals course.StudentID
select new { StudentName = student.Name, CourseName = course.CourseName };
Console.WriteLine("Student Courses:");
foreach (var item in studentCourses)
{
Console.WriteLine($"{item.StudentName} is enrolled in {item.CourseName}");
}
/* Output:
Student Courses:
Alice is enrolled in Math
Alice is enrolled in Science
Bob is enrolled in History
Charlie is enrolled in English
Charlie is enrolled in Physics
*/
Let Clause for Temporary Variables
The let
clause allows you to introduce temporary variables within your query:
var words = new List<string> { "apple", "banana", "cherry", "date", "elderberry" };
var longWords = from word in words
let length = word.Length
where length > 5
orderby length
select new { Word = word, Length = length };
foreach (var item in longWords)
{
Console.WriteLine($"{item.Word}: {item.Length} characters");
}
/* Output:
banana: 6 characters
cherry: 6 characters
elderberry: 10 characters
*/
Real-World Application Example
Let's look at a more complex example that represents a real-world scenario:
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Sample product data
List<Product> products = new List<Product>
{
new Product { ID = 1, Name = "Laptop", Category = "Electronics", Price = 899.99m, Stock = 12 },
new Product { ID = 2, Name = "Smartphone", Category = "Electronics", Price = 599.99m, Stock = 25 },
new Product { ID = 3, Name = "Headphones", Category = "Electronics", Price = 149.99m, Stock = 40 },
new Product { ID = 4, Name = "Desk", Category = "Furniture", Price = 249.99m, Stock = 5 },
new Product { ID = 5, Name = "Chair", Category = "Furniture", Price = 119.99m, Stock = 8 },
new Product { ID = 6, Name = "Bookshelf", Category = "Furniture", Price = 179.99m, Stock = 3 },
new Product { ID = 7, Name = "Monitor", Category = "Electronics", Price = 349.99m, Stock = 15 },
new Product { ID = 8, Name = "Coffee Table", Category = "Furniture", Price = 159.99m, Stock = 7 }
};
// Find expensive electronics (price > 300) with low stock (< 20)
var expensiveElectronicsLowStock = from product in products
where product.Category == "Electronics"
&& product.Price > 300
&& product.Stock < 20
orderby product.Stock
select new
{
ProductName = product.Name,
Price = product.Price,
InStock = product.Stock
};
Console.WriteLine("Expensive electronics with low stock:");
foreach (var item in expensiveElectronicsLowStock)
{
Console.WriteLine($"{item.ProductName}: ${item.Price} - {item.InStock} in stock");
}
// Group products by category and calculate statistics
var productStatsByCategory = from product in products
group product by product.Category into categoryGroup
select new
{
Category = categoryGroup.Key,
TotalProducts = categoryGroup.Count(),
AveragePrice = categoryGroup.Average(p => p.Price),
TotalStock = categoryGroup.Sum(p => p.Stock),
MostExpensiveItem = categoryGroup.OrderByDescending(p => p.Price).First().Name
};
Console.WriteLine("\nProduct statistics by category:");
foreach (var stat in productStatsByCategory)
{
Console.WriteLine($"Category: {stat.Category}");
Console.WriteLine($" Total products: {stat.TotalProducts}");
Console.WriteLine($" Average price: ${stat.AveragePrice:F2}");
Console.WriteLine($" Total stock: {stat.TotalStock} units");
Console.WriteLine($" Most expensive item: {stat.MostExpensiveItem}");
}
}
}
class Product
{
public int ID { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}
Output:
Expensive electronics with low stock:
Laptop: $899.99 - 12 in stock
Monitor: $349.99 - 15 in stock
Product statistics by category:
Category: Electronics
Total products: 4
Average price: $499.99
Total stock: 92 units
Most expensive item: Laptop
Category: Furniture
Total products: 4
Average price: $177.49
Total stock: 23 units
Most expensive item: Desk
This example demonstrates how LINQ Query Syntax can be used in an e-commerce application to:
- Find products matching specific criteria (expensive electronics with low stock)
- Group products and calculate statistics (average price, total stock, etc.)
Query Syntax vs. Method Syntax
While this tutorial focuses on Query Syntax, it's worth noting that the same functionality can be achieved using Method Syntax (also called "fluent syntax"). Here's a comparison:
Query Syntax:
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
Method Syntax:
var evenNumbers = numbers.Where(num => num % 2 == 0);
Most developers use a combination of both syntaxes:
- Query Syntax is often preferred for readability in complex queries
- Method Syntax is often preferred for simpler operations and when using lambda expressions
Key Points to Remember
- LINQ Query Syntax uses SQL-like keywords (
from
,where
,select
, etc.) - The query always starts with
from
and typically ends withselect
orgroup
- Unlike SQL, the
select
clause comes at the end, not the beginning - LINQ queries don't execute until you iterate through the results (deferred execution)
- Query expressions are converted to method calls during compilation
Summary
LINQ Query Syntax provides a powerful, SQL-like way to query and manipulate data in C#. It allows you to:
- Filter data using the
where
clause - Transform data with
select
- Sort results with
orderby
- Group data with
group by
- Join multiple collections
- Create temporary variables with
let
By mastering LINQ Query Syntax, you can write cleaner, more readable code for data manipulation tasks, reducing the amount of boilerplate code and making your intentions clearer.
Exercises
To practice your LINQ Query Syntax skills, try these exercises:
- Create a list of integers and write a query to find all numbers divisible by 3 or 5.
- Create a list of strings and write a query to find all strings that start with 'a' and have a length greater than 5.
- Create two lists (e.g., customers and orders) and write a query that joins them.
- Create a list of objects and write a query that groups them by a property and calculates aggregate values.
- Write a query that uses the
let
clause to create a temporary calculation.
Additional Resources
Happy coding with LINQ Query Syntax!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)