Introduction to LINQ in C#

Ravi Patel
5 min readAug 9, 2024

--

Language Integrated Query (LINQ) is one of the most powerful features of C#. It allows developers to write queries directly within the C# language to manipulate and query data from different data sources like collections, databases, XML documents, and more. LINQ makes it easier to work with data by providing a consistent and readable way to query and transform data.

This blog will guide you step-by-step through the basics of LINQ, starting from the syntax to more advanced topics, all explained in a straightforward manner.

Why LINQ?

Before diving into the syntax, let’s understand why LINQ is so important:

  • Unified Syntax: LINQ provides a uniform syntax to query different data sources (e.g., arrays, lists, XML, databases).
  • Readability: LINQ queries are often more readable and concise than equivalent loops or other code constructs.
  • Strongly Typed: LINQ is integrated into C#, meaning that you get compile-time checking and IntelliSense support.
  • Declarative Approach: LINQ allows you to describe what you want to do with the data rather than how to do it, leading to clearer and more maintainable code.

Basic Concepts of LINQ

Before we start writing LINQ queries, let’s understand some basic concepts.

  1. Data Source: The collection or sequence of data that you want to query. It could be an array, list, database table, etc.
  2. Query: The set of operations that you define to fetch the data you need from the data source.
  3. Execution: When the query is run, it pulls the data from the data source based on the query conditions.

Getting Started with LINQ

Let’s start with a simple example. Assume we have a list of integers, and we want to find all even numbers.

using System;
using System.Collections.Generic;
using System.Linq;

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
var evenNumbers = from num in numbers
where num % 2 == 0
select num;

// Execution
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
}
}

Explanation:

  • Data Source: numbers is our data source, a list of integers.
  • Query: The LINQ query starts with the from keyword and is followed by where and select clauses.
  • from num in numbers: Iterates over each item in the numbers list.
  • where num % 2 == 0: Filters the numbers that are divisible by 2 (even numbers).
  • select num: Selects the numbers that meet the condition.

When you run this code, it will output:

2
4
6
8
10

LINQ Syntax

LINQ provides two types of syntax:

Query Syntax: This is similar to SQL and is more readable for those familiar with SQL.

Example:

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

Method Syntax: This is more compact and is often used in method chaining.

var evenNumbers = numbers.Where(num => num % 2 == 0);

Both syntaxes are equivalent, and you can use whichever you find more readable.

Key LINQ Operators

LINQ provides a rich set of operators to manipulate data. Here are some of the most commonly used ones:

Where: Filters elements based on a condition.

var evenNumbers = numbers.Where(num => num % 2 == 0);

Select: Projects each element of a sequence into a new form.

var squares = numbers.Select(num => num * num);

OrderBy/OrderByDescending: Sorts the elements in ascending or descending order.

var sortedNumbers = numbers.OrderBy(num => num);

GroupBy: Groups elements that share a common attribute.

var groupedNumbers = numbers.GroupBy(num => num % 2);

Join: Joins two sequences based on a key.

var joinedData = from num in numbers
join num2 in otherNumbers on num equals num2
select new { num, num2 };

Aggregate: Performs an operation on each element of the sequence and returns a single result.

var sum = numbers.Aggregate((a, b) => a + b);

Working with Different Data Sources

LINQ is not limited to collections; it can query various data sources like:

LINQ to Objects: Queries collections like arrays, lists, etc.

var result = from num in numbers where num > 5 select num;

LINQ to SQL: Queries relational databases.

var query = from p in context.Products where p.Price > 100 select p;

LINQ to XML: Queries XML documents.

var xmlQuery = from x in xmlDoc.Elements("Book") where (string)x.Element("Author") == "AuthorName" select x;

LINQ to Entities: Queries Entity Framework models.

var xmlQuery = from x in xmlDoc.Elements("Book") where (string)x.Element("Author") == "AuthorName" select x;

Deferred Execution vs. Immediate Execution

One important concept in LINQ is Deferred Execution. LINQ queries are not executed when they are defined but when they are iterated over (e.g., using a foreach loop). This is called deferred execution.

var query = from num in numbers where num > 5 select num;
// Query is not executed here

foreach (var num in query)
{
Console.WriteLine(num); // Query is executed here
}

Immediate Execution occurs when methods like ToList(), ToArray(), First(), or Count() are called, which forces the query to execute immediately.

var immediateExecution = numbers.Where(num => num > 5).ToList(); // Executed immediately

Advanced LINQ Features

Anonymous Types: LINQ allows you to create anonymous types directly within a query.

var results = from num in numbers
select new { Number = num, Square = num * num };

Let Keyword: Allows you to store the result of a subquery in a variable to reuse it within the same query.

var result = from num in numbers
let square = num * num
where square > 10
select square;

Join: Used to combine multiple data sources based on a common key.

var joinResult = from p in products
join c in categories on p.CategoryId equals c.Id
select new { p.Name, c.CategoryName };

GroupJoin: Combines data and groups the results.

var groupJoin = from c in categories
join p in products on c.Id equals p.CategoryId into productGroup
select new { c.CategoryName, Products = productGroup };

Cross Join: Combines each element of one data source with every element of another data source.

var crossJoin = from p in products
from c in categories
select new { p.Name, c.CategoryName };

Zip: Combines two sequences by merging their elements based on their position.

var zipped = numbers.Zip(otherNumbers, (first, second) => first + second);

Best Practices for LINQ

  1. Use Method Syntax for Complex Queries: Method syntax can be more powerful and concise, especially for complex queries.
  2. Avoid Repeated Queries: Store the result of a query if you need to use it multiple times to avoid performance issues.
  3. Use Deferred Execution Wisely: Understand when your queries will execute to avoid unexpected behavior.
  4. Optimize Performance: Consider the performance impact of your queries, especially with large datasets or complex operations.

Conclusion

LINQ is a powerful tool in C# that makes working with data more intuitive and readable. Whether you’re querying collections, databases, or XML, LINQ provides a consistent way to handle data. By understanding and mastering LINQ, you can write more efficient, maintainable, and cleaner code.

Remember, the best way to learn LINQ is by practicing. Try different queries with your data sets, experiment with both query and method syntax, and explore the various LINQ operators.

Happy coding!

--

--