LINQ Fundamentals

Language-Integrated Query (LINQ) is the name for a group of technologies based on the integration of querying capabilities directly into the .NET Framework. LINQ enables you to write queries to objects, XML, databases, and other data sources in a strongly typed and familiar C# or Visual Basic syntax.

Introduction to LINQ

Before LINQ, developers had to use different query languages and syntax for different data sources. For example, SQL for relational databases, XPath for XML, and custom APIs for collections. This inconsistency led to increased complexity and a steeper learning curve.

LINQ provides a unified and consistent way to query data, regardless of its source. It offers:

Query Syntax

LINQ query syntax is designed to be similar to SQL. It starts with a from clause, followed by optional where, select, orderby, group by, and other clauses.

The general structure is:


var query = from variable in dataSource
            where condition
            orderby key
            select projection;
            

Example: Filtering and Ordering a List of Numbers


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

public class LinqQuerySyntaxExample
{
    public static void Main(string[] args)
    {
        List numbers = new List { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

        // LINQ query syntax
        var evenNumbersQuery = from num in numbers
                               where num % 2 == 0
                               orderby num
                               select num;

        Console.WriteLine("Even numbers in ascending order:");
        foreach (var number in evenNumbersQuery)
        {
            Console.Write(number + " ");
        }
        // Output: 0 2 4 6 8
    }
}
                

Method Syntax

LINQ also provides a method syntax, which uses extension methods defined in the System.Linq.Enumerable class for LINQ to Objects or System.Linq.Queryable for LINQ to Entities/SQL. This syntax is often used when query syntax is not applicable or when a more functional programming style is desired.

Query syntax is often translated into method syntax by the C# compiler.

Example: Filtering and Ordering a List of Numbers (Method Syntax)


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

public class LinqMethodSyntaxExample
{
    public static void Main(string[] args)
    {
        List numbers = new List { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

        // LINQ method syntax
        var evenNumbersQuery = numbers.Where(num => num % 2 == 0)
                                      .OrderBy(num => num);

        Console.WriteLine("Even numbers in ascending order (Method Syntax):");
        foreach (var number in evenNumbersQuery)
        {
            Console.Write(number + " ");
        }
        // Output: 0 2 4 6 8
    }
}
                

Query Syntax vs. Method Syntax

While both syntaxes achieve the same results, there are key differences:

Standard Query Operators

LINQ offers a rich set of standard query operators that perform common data manipulation tasks. These operators can be categorized as:

Each operator has a specific purpose and can be used in combination to build complex queries.

Deferred Execution

One of the key features of LINQ is deferred execution. This means that the actual execution of a LINQ query is delayed until the query results are iterated over. This has several implications:

Note: Aggregation methods like Count(), Sum(), Max(), Min(), Average(), and element methods like First(), Single() perform immediate execution.

Example of Deferred Execution:


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

public class DeferredExecutionExample
{
    public static void Main(string[] args)
    {
        List numbers = new List { 1, 2, 3, 4, 5 };

        // Define the query - no execution yet
        var query = numbers.Where(n => n > 2);

        Console.WriteLine("Query defined. Now adding more elements.");
        numbers.Add(6); // Data source modified

        Console.WriteLine("Iterating through the query:");
        foreach (var number in query) // Execution happens here
        {
            Console.Write(number + " ");
        }
        // Output: 3 4 5 6
    }
}
                

Creating Custom Operators

While LINQ provides many standard operators, you can also create your own custom query operators using extension methods. This is particularly useful for encapsulating complex or frequently used query logic.

To create a custom operator, you typically write an extension method for IEnumerable<T> (for LINQ to Objects) or IQueryable<T> (for LINQ to providers like Entity Framework).

Tip: For LINQ to Objects, always ensure your custom operator is deferred execution unless it's explicitly an aggregation or element operator.

Example: A simple custom operator to get numbers greater than a threshold.


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

public static class CustomLinqExtensions
{
    public static IEnumerable<int> GreaterThan(this IEnumerable<int> source, int threshold)
    {
        if (source == null)
            throw new ArgumentNullException(nameof(source));

        return source.Where(n => n > threshold);
    }
}

public class CustomOperatorExample
{
    public static void Main(string[] args)
    {
        List<int> numbers = new List<int> { 1, 5, 2, 8, 3, 9 };

        var result = numbers.GreaterThan(4); // Using custom operator

        Console.WriteLine("Numbers greater than 4:");
        foreach (var number in result)
        {
            Console.Write(number + " ");
        }
        // Output: 5 8 9
    }
}
                

Next Steps

Understanding LINQ fundamentals is crucial for leveraging its power. Explore specific LINQ providers like LINQ to Objects, LINQ to SQL, and LINQ to XML for practical applications. Continue your learning with more advanced topics like custom aggregate functions and complex join operations.