Introduction to LINQ

LINQ is a major feature introduced in the .NET Framework 3.5 and C# 3.0 that revolutionized how data is queried in .NET applications.

What is LINQ?

LINQ, or Language Integrated Query, is a powerful feature in .NET that allows querying and manipulating data collections directly in C#. It works with arrays, lists, XML, databases, and other data sources using a consistent, readable syntax.

Imagine an application needs to find all customers from a list who live in 'New York' and have placed an order in the last 30 days.

Without LINQ

List<Customer> nyCustomers = new List<Customer>();
foreach (var customer in allCustomers)
{
    if (customer.City == "New York")
    {
        nyCustomers.Add(customer);
    }
}

With LINQ

var nyCustomers = from customer in allCustomers
                  where customer.City == "New York" 
                  // && customer.Orders.Any(o => o.OrderDate >= thirtyDaysAgo)
                  orderby customer.LastName
                  select customer;

Before LINQ, querying data required different approaches for each data source: loops for lists, SQL for databases, and XML APIs for XML. This caused messy, hard-to-maintain code. LINQ solves this problem by providing a unified way to query any data in C# with less code and fewer errors.

Types of LINQ Syntax

LINQ in .NET provides two main types of syntax for writing queries. Each has its own style, use cases, and practical implications.

1. Query Syntax (Declarative Syntax)

Query syntax resembles SQL like statements. It is declarative, describing what to retrieve rather than how to retrieve it. It is often preferred for simple, stand-alone queries that involve filtering, ordering, and projection. Query Syntax uses C# keywords like from, where, select, and orderby to define a query block. It is a declarative style, meaning it describes what data one wants, rather than how to get it.

Example Query Syntax

int[] numbers = { 1, 2, 3, 4, 5, 6 };

// Select even numbers using query syntax
var evenNumbers = from n in numbers
                  where n % 2 == 0
                  select n;

foreach(var num in evenNumbers)
{
    Console.WriteLine(num); // Output: 2, 4, 6
}

2. Method Syntax (Fluent Syntax / Lambda Syntax)

This syntax uses Extension Methods defined in System.Linq combined with lambda expressions applied directly to the data source (IEnumerable<T> or IQueryable<T>). Method Syntax, also known as Fluent Syntax, involves chaining standard query operator methods (like .Where(), .Select(), .OrderBy()) one after the other. It is an object-oriented style that leverages the C# language features extensively. Preferred when using advanced LINQ features like Join or GroupBy.

Example Method Syntax:

int[] numbers = { 1, 2, 3, 4, 5, 6 };

// Select even numbers using method syntax
var evenNumbers = numbers.Where(n => n % 2 == 0);

foreach(var num in evenNumbers)
{
    Console.WriteLine(num); // Output: 2, 4, 6
}

Standard Query Operators (SQOs)

The Standard Query Operators (SQOs) are the foundational set of methods that form the core of LINQ. They are a collection of over 50 methods that provide common query capabilities like filtering, projection, aggregation, and grouping.

These operators are implemented as Extension Methods on the interfaces that represent the data source:

  1. IEnumerable<T>: Used for LINQ to Objects (in-memory collections).

  2. IQueryable<T>: Used for LINQ to Entities (external data sources like databases).

The methods share the same names (e.g., Where, Select), but their implementations differ based on the interface they extend:

If a deep understanding of IQueryable and IEnumerable is desired, read the full article at: IEnumerable vs IQueryable

Categorization of Key SQOs

The Standard Query Operators are typically grouped by the type of operation they perform:

1. Filtering Operators (Selecting a Subset)

These operators restrict the result set based on a condition, similar to the SQL WHERE clause.

Example: employees.Where(e => e.IsActive && e.Salary > 70000)

2. Projection Operators (Shaping the Data)

These operators transform elements of a sequence into a new form, similar to the SQL SELECT clause.

Example: products.Select(p => new { p.Name, SalePrice = p.Price * 0.9 }) (Creating an Anonymous Type).

3. Ordering Operators (Sorting the Results)

These operators sort the elements of a sequence.

Example: employees.OrderBy(e => e.LastName).ThenBy(e => e.FirstName)

4. Grouping Operators

These operators group elements based on a common key.

Example: orders.GroupBy(o => o.CustomerID) (Groups all orders, where the key is the Customer ID).


5. Set Operators

These operators compare elements between two sequences based on set theory principles.

6. Quantifier Operators (Boolean Checks)

These operators check if a condition is met for elements in a sequence and return a boolean result. They are highly efficient as they usually stop processing as soon as the result is determined.

Example: orders.Any(o => o.Status == "Pending")
Example: grades.All(g => g >= 60)

7. Aggregation Operators (Calculating a Single Value)

These operators compute a single value from a sequence of values.

8. Partitioning Operators (Paging)

These operators divide a sequence into two parts and return one of the parts. They are essential for implementing paging functionality.

Example (Paging): data.Skip( (pageNumber - 1) * pageSize ).Take(pageSize)


9. Element Operators (Single Result)

These operators return a single element from a sequence.

Difference Between EF Query and LINQ Query

FeatureLINQ QueryEntity Framework (EF) Query
DefinitionLINQ (Language Integrated Query) is a .NET language feature that allows querying collections, XML, JSON, or databases.EF Query is a database-specific query written using LINQ or lambda expressions through Entity Framework ORM, which translates it into SQL.
Scope / Data SourceCan query in-memory collections (IEnumerable<T>) like arrays, lists, dictionaries, XML, or JSON.Queries relational databases via DbContext, typically using DbSet<T> mapped to database tables.
ExecutionLINQ to Objects executes in-memory, directly in C#. Execution is deferred until enumeration (foreach).EF Query uses IQueryable, translates LINQ expressions into SQL, executed on the database server.
PerformanceOperates on local data, faster for small collections, but can be slower for large datasets due to in-memory processing.Optimized by EF; database-side execution reduces memory overhead and leverages indexes. Poorly written queries may generate inefficient SQL.
SyntaxCan use query syntax (from...select) or method syntax (Where, Select). Works for any collection type.Typically method syntax with DbSet<T> (Where, Select, Include), but query syntax also works. EF adds Include() for navigation properties.
Type SafetyStrongly typed in C#, works at compile time.Also strongly typed. EF maps C# types to database schema.
Deferred ExecutionYes, queries are executed when enumerated.Yes, queries are executed when enumerated (ToList(), FirstOrDefault(), etc.). EF queries are translated to SQL at execution time.

How LINQ Works

LINQ works

Conclusion

LINQ (Language Integrated Query) revolutionizes the way data is queried and manipulated in .NET by providing a unified, expressive, and type-safe approach.LINQ not only simplifies data operations but also encourages consistency across applications.