.NET LINQ Query Syntax
Introduction
LINQ (Language Integrated Query) is one of the most powerful features in .NET, allowing developers to query data from various sources using a consistent syntax. LINQ provides two main syntaxes for writing queries: the method syntax (also known as fluent syntax) and the query syntax.
In this tutorial, we'll focus on LINQ's query syntax, which resembles SQL and provides a more readable, declarative approach to querying collections. Query syntax is particularly helpful for beginners and those familiar with SQL as it presents queries in a more human-readable format.
What is LINQ Query Syntax?
LINQ query syntax is a SQL-like syntax integrated directly into C# that allows you to write queries against collections. It starts with a from
clause and typically ends with a select
or group
clause, with various optional clauses in between.
Here's a basic structure:
var query = from element in collection
where condition
select element;
This approach makes complex data manipulations more readable and maintainable compared to traditional loops and conditions.
Basic Query Syntax Elements
Let's break down the essential components of LINQ query syntax:
1. from
Clause
The from
clause specifies the data source and a range variable:
from student in students
student
: The range variable (represents each item during iteration)students
: The data source (collection being queried)
2. where
Clause
The where
clause filters elements based on a condition:
where student.Age > 18
3. select
Clause
The select
clause specifies what data to retrieve:
select student
// or
select new { student.Name, student.Age }
Your First LINQ Query
Let's see a complete example:
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 list of integers
- We create a LINQ query that selects numbers where
num % 2 == 0
(even numbers) - We execute the query by iterating through the results
Ordering Results with orderby
The orderby
clause lets you sort results:
List<Student> students = new List<Student>
{
new Student { Name = "Alice", Age = 19 },
new Student { Name = "Bob", Age = 21 },
new Student { Name = "Charlie", Age = 18 }
};
var sortedStudents = from student in students
orderby student.Age ascending
select student;
// Output:
// Charlie, 18
// Alice, 19
// Bob, 21
// For descending order:
var descendingOrder = from student in students
orderby student.Age descending
select student;
Grouping Results with group by
The group by
clause allows you to group results by a key:
List<Student> students = new List<Student>
{
new Student { Name = "Alice", Grade = "A" },
new Student { Name = "Bob", Grade = "B" },
new Student { Name = "Charlie", Grade = "A" },
new Student { Name = "Dave", Grade = "B" }
};
var groupedStudents = from student in students
group student by student.Grade into gradeGroup
select new { Grade = gradeGroup.Key, Students = gradeGroup };
foreach (var group in groupedStudents)
{
Console.WriteLine($"Grade {group.Grade}:");
foreach (var student in group.Students)
{
Console.WriteLine($"- {student.Name}");
}
}
// Output:
// Grade A:
// - Alice
// - Charlie
// Grade B:
// - Bob
// - Dave
Joining Collections with join
The join
clause allows you to combine data from multiple collections:
List<Student> students = new List<Student>
{
new Student { Id = 1, Name = "Alice" },
new Student { Id = 2, Name = "Bob" }
};
List<Course> courses = new List<Course>
{
new Course { StudentId = 1, CourseName = "Math" },
new Course { StudentId = 1, CourseName = "Physics" },
new Course { StudentId = 2, CourseName = "Chemistry" }
};
var studentCourses = from student in students
join course in courses
on student.Id equals course.StudentId
select new { StudentName = student.Name, Course = course.CourseName };
foreach (var item in studentCourses)
{
Console.WriteLine($"{item.StudentName} is enrolled in {item.Course}");
}
// Output:
// Alice is enrolled in Math
// Alice is enrolled in Physics
// Bob is enrolled in Chemistry
Let Clause for Temporary Variables
The let
clause creates a temporary variable within a query:
List<string> names = new List<string> { "Alice Smith", "Bob Johnson", "Charlie Brown" };
var nameInfo = from fullName in names
let nameParts = fullName.Split(' ')
let firstName = nameParts[0]
let lastName = nameParts[1]
select new { FirstName = firstName, LastName = lastName, FullName = fullName };
foreach (var person in nameInfo)
{
Console.WriteLine($"First name: {person.FirstName}, Last name: {person.LastName}");
}
// Output:
// First name: Alice, Last name: Smith
// First name: Bob, Last name: Johnson
// First name: Charlie, Last name: Brown
Practical Example: Employee Management System
Let's create a more complex real-world example for an employee management system:
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Sample data
List<Department> departments = new List<Department>
{
new Department { Id = 1, Name = "Engineering" },
new Department { Id = 2, Name = "Marketing" },
new Department { Id = 3, Name = "HR" }
};
List<Employee> employees = new List<Employee>
{
new Employee { Id = 101, Name = "Alice", DeptId = 1, Salary = 75000, YearsOfService = 3 },
new Employee { Id = 102, Name = "Bob", DeptId = 2, Salary = 65000, YearsOfService = 2 },
new Employee { Id = 103, Name = "Charlie", DeptId = 1, Salary = 85000, YearsOfService = 5 },
new Employee { Id = 104, Name = "Diana", DeptId = 3, Salary = 70000, YearsOfService = 4 },
new Employee { Id = 105, Name = "Eva", DeptId = 2, Salary = 90000, YearsOfService = 6 },
new Employee { Id = 106, Name = "Frank", DeptId = 1, Salary = 72000, YearsOfService = 2 }
};
// Query 1: Find employees who earn more than 70,000 and have been with the company for at least 3 years
var seniorHighEarners = from emp in employees
where emp.Salary > 70000 && emp.YearsOfService >= 3
orderby emp.Salary descending
select new { emp.Name, emp.Salary, emp.YearsOfService };
Console.WriteLine("Senior high-earning employees:");
foreach (var emp in seniorHighEarners)
{
Console.WriteLine($"{emp.Name} - ${emp.Salary}, {emp.YearsOfService} years");
}
// Query 2: Department-wise employee count and average salary
var deptStats = from emp in employees
group emp by emp.DeptId into deptGroup
join dept in departments
on deptGroup.Key equals dept.Id
select new {
Department = dept.Name,
EmployeeCount = deptGroup.Count(),
AverageSalary = deptGroup.Average(e => e.Salary)
};
Console.WriteLine("\nDepartment statistics:");
foreach (var stat in deptStats)
{
Console.WriteLine($"{stat.Department}: {stat.EmployeeCount} employees, Avg salary: ${stat.AverageSalary:F2}");
}
// Query 3: Find highest paid employee in each department
var topEarners = from dept in departments
join emp in employees
on dept.Id equals emp.DeptId
group emp by dept.Name into deptGroup
let maxSalary = deptGroup.Max(e => e.Salary)
let topEmployee = deptGroup.First(e => e.Salary == maxSalary)
select new {
Department = deptGroup.Key,
EmployeeName = topEmployee.Name,
Salary = maxSalary
};
Console.WriteLine("\nHighest paid employee in each department:");
foreach (var topEarner in topEarners)
{
Console.WriteLine($"{topEarner.Department}: {topEarner.EmployeeName} (${topEarner.Salary})");
}
}
}
class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int DeptId { get; set; }
public double Salary { get; set; }
public int YearsOfService { get; set; }
}
Output:
Senior high-earning employees:
Eva - $90000, 6 years
Charlie - $85000, 5 years
Diana - $70000, 4 years
Department statistics:
Engineering: 3 employees, Avg salary: $77333.33
Marketing: 2 employees, Avg salary: $77500.00
HR: 1 employees, Avg salary: $70000.00
Highest paid employee in each department:
Engineering: Charlie ($85000)
Marketing: Eva ($90000)
HR: Diana ($70000)
When to Use Query Syntax vs. Method Syntax
While both syntaxes can achieve the same results, each has its advantages:
Use Query Syntax when:
- You're writing complex queries with multiple operations
- You're more familiar with SQL
- Your query involves joins, grouping, or filtering with multiple conditions
Use Method Syntax when:
- You're performing simple operations (single filter, projection)
- You need to use LINQ operators that don't have query syntax equivalents (like
Skip
,Take
,Distinct
) - You prefer chaining methods for readability
Summary
LINQ query syntax provides a powerful, readable way to query collections in .NET. In this tutorial, we've covered:
- The basic structure of LINQ query syntax
- How to filter, project, and sort data
- Grouping and joining collections
- Using temporary variables with the
let
clause - A practical example with complex queries
Learning LINQ query syntax is essential for .NET developers as it makes data manipulation more concise and readable. While method syntax is equally powerful, query syntax often provides better readability for complex queries, especially for those familiar with SQL.
Additional Resources and Exercises
Resources
Exercises
-
Basic Exercise: Create a list of products with properties Name, Price, and Category. Write a LINQ query to find all products in a specific category with a price less than a given amount.
-
Intermediate Exercise: Create two lists: Authors and Books, where Books contains an AuthorId. Write a LINQ query to find all authors who have written more than 3 books and order them by the number of books they've written.
-
Advanced Exercise: Create three related collections: Students, Courses, and Enrollments (joining Students and Courses). Write a LINQ query to find the most popular course (with the most enrollments) and list all students enrolled in it.
By practicing these exercises, you'll develop a strong grasp of LINQ query syntax and be able to apply it effectively in your .NET projects.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)