Skip to main content

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:

csharp
var result = from [variable] in [datasource]
[optional clauses]
select [expression];

Let's break down the components:

  1. from [variable] in [datasource] - Specifies the data source and a range variable.
  2. [optional clauses] - Can include filtering (where), sorting (orderby), grouping (group by), etc.
  3. 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:

csharp
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:

  1. Define a data source (a list of integers)
  2. Write a query to filter even numbers using the where clause
  3. Retrieve the results using the select clause
  4. Iterate through the results to display them

Example 2: Transforming Data

We can also transform our data using LINQ queries:

csharp
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:

csharp
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:

csharp
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:

csharp
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:

csharp
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:

csharp
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:

csharp
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:

  1. Find products matching specific criteria (expensive electronics with low stock)
  2. 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:

csharp
var evenNumbers = from num in numbers
where num % 2 == 0
select num;

Method Syntax:

csharp
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

  1. LINQ Query Syntax uses SQL-like keywords (from, where, select, etc.)
  2. The query always starts with from and typically ends with select or group
  3. Unlike SQL, the select clause comes at the end, not the beginning
  4. LINQ queries don't execute until you iterate through the results (deferred execution)
  5. 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:

  1. Create a list of integers and write a query to find all numbers divisible by 3 or 5.
  2. Create a list of strings and write a query to find all strings that start with 'a' and have a length greater than 5.
  3. Create two lists (e.g., customers and orders) and write a query that joins them.
  4. Create a list of objects and write a query that groups them by a property and calculates aggregate values.
  5. 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! :)