Sorting Query Results with LINQ (orderby, ascending, descending) in C#

Controlling Data Order

If you've ever displayed a list of products sorted by price only to have users complain they can't find what they need, you know how poor sorting hurts usability. Unsorted data forces users to scan entire lists manually. They expect results alphabetically by name or numerically by price, but your app shows items in random database order.

This article shows how LINQ's orderby, ascending, and descending keywords solve this by letting you declare sort order right in your queries. You get predictable results that match user expectations, whether sorting by one field or multiple levels. Clear sorting improves user experience and makes your data more accessible.

You'll learn basic ascending and descending sorts, multi-level sorting with ThenBy behavior, and when to use query syntax versus method syntax. By the end, you'll handle any sorting scenario from simple price lists to complex reports with multiple sort columns.

Sorting with orderby and ascending

The orderby keyword arranges query results based on one or more properties. By default, it sorts in ascending order (A-Z, 0-9, oldest to newest). You can make this explicit with the ascending keyword, but it's optional since ascending is the default behavior.

Place orderby after your where clause and before select. The compiler translates this to OrderBy method calls behind the scenes. For value types and strings, LINQ uses the natural comparison operators defined for those types.

BasicSort.cs
var products = new List<Product>
{
    new() { Name = "Keyboard", Price = 79.99m, Category = "Electronics" },
    new() { Name = "Mouse", Price = 25.50m, Category = "Electronics" },
    new() { Name = "Desk", Price = 299.00m, Category = "Furniture" },
    new() { Name = "Chair", Price = 199.99m, Category = "Furniture" },
    new() { Name = "Monitor", Price = 349.00m, Category = "Electronics" }
};

// Sort by price, lowest to highest (ascending is default)
var byPrice = from p in products
              orderby p.Price
              select p;

Console.WriteLine("Sorted by Price:");
foreach (var product in byPrice)
{
    Console.WriteLine($"{product.Name}: ${product.Price}");
}

// Output:
// Mouse: $25.50
// Keyboard: $79.99
// Chair: $199.99
// Desk: $299.00
// Monitor: $349.00

record Product
{
    public string Name { get; init; }
    public decimal Price { get; init; }
    public string Category { get; init; }
}

The query returns products sorted from cheapest to most expensive. Mouse appears first at $25.50, followed by Keyboard, then the higher-priced items. This natural ordering helps users find budget options quickly.

Reversing Order with descending

The descending keyword reverses sort order to show largest values first (Z-A, 9-0, newest to oldest). This is useful for showing premium items, recent dates, or highest ratings at the top of lists.

Add descending immediately after the property you're sorting by. You can mix ascending and descending in multi-level sorts to create complex ordering logic that matches business requirements.

DescendingSort.cs
var sales = new List<SaleRecord>
{
    new() { Product = "Laptop", Amount = 1200.00m, SaleDate = new DateTime(2025, 11, 1) },
    new() { Product = "Phone", Amount = 899.00m, SaleDate = new DateTime(2025, 11, 3) },
    new() { Product = "Tablet", Amount = 499.00m, SaleDate = new DateTime(2025, 11, 2) },
    new() { Product = "Watch", Amount = 399.00m, SaleDate = new DateTime(2025, 11, 4) }
};

// Show highest sales first
var topSales = from s in sales
               orderby s.Amount descending
               select s;

Console.WriteLine("Top Sales:");
foreach (var sale in topSales)
{
    Console.WriteLine($"{sale.Product}: ${sale.Amount}");
}

// Most recent sales first
var recentSales = from s in sales
                  orderby s.SaleDate descending
                  select s;

Console.WriteLine("\nRecent Sales:");
foreach (var sale in recentSales)
{
    Console.WriteLine($"{sale.Product} on {sale.SaleDate:d}");
}

record SaleRecord
{
    public string Product { get; init; }
    public decimal Amount { get; init; }
    public DateTime SaleDate { get; init; }
}

The first query shows Laptop at the top with the highest amount, while the second query displays Watch first as the most recent sale. Descending order highlights important items that users care about most.

Multi-Level Sorting

Real applications often need to sort by multiple criteria. For example, sort products by category first, then by price within each category. LINQ lets you chain sort criteria using commas. The first criterion determines primary order, and subsequent criteria break ties.

Each level can independently use ascending or descending. This gives you precise control over result ordering without writing complex comparison logic manually.

MultiSort.cs
var inventory = new List<InventoryItem>
{
    new() { Name = "Mouse", Category = "Electronics", Price = 25.50m, Stock = 50 },
    new() { Name = "Keyboard", Category = "Electronics", Price = 79.99m, Stock = 30 },
    new() { Name = "Desk", Category = "Furniture", Price = 299.00m, Stock = 10 },
    new() { Name = "Chair", Category = "Furniture", Price = 199.99m, Stock = 15 },
    new() { Name = "Monitor", Category = "Electronics", Price = 349.00m, Stock = 20 }
};

// Group by category (A-Z), then show expensive items first within each category
var sorted = from item in inventory
             orderby item.Category ascending, item.Price descending
             select item;

Console.WriteLine("Inventory by Category (Highest Price First):");
foreach (var item in sorted)
{
    Console.WriteLine($"{item.Category} - {item.Name}: ${item.Price}");
}

// Output:
// Electronics - Monitor: $349.00
// Electronics - Keyboard: $79.99
// Electronics - Mouse: $25.50
// Furniture - Desk: $299.00
// Furniture - Chair: $199.99

record InventoryItem
{
    public string Name { get; init; }
    public string Category { get; init; }
    public decimal Price { get; init; }
    public int Stock { get; init; }
}

Results group by category alphabetically (Electronics before Furniture), then within each category show the most expensive items first. This two-level sort creates organized, hierarchical output that's easy to scan.

Query Syntax vs Method Syntax

You can express the same sorting using method syntax with OrderBy, OrderByDescending, ThenBy, and ThenByDescending. Query syntax with orderby feels more SQL-like and reads naturally when you're already using from/where clauses. Method syntax works better when chaining with other LINQ operations or when you prefer fluent style.

Both compile to identical IL code, so choose based on readability and consistency with your codebase. Many teams prefer query syntax for complex queries and method syntax for simple transformations.

MethodSyntax.cs
var employees = new List<Employee>
{
    new() { Name = "Alice", Department = "Engineering", Salary = 95000 },
    new() { Name = "Bob", Department = "Sales", Salary = 75000 },
    new() { Name = "Carol", Department = "Engineering", Salary = 105000 },
    new() { Name = "David", Department = "Sales", Salary = 80000 }
};

// Query syntax
var querySort = from e in employees
                orderby e.Department, e.Salary descending
                select e;

// Equivalent method syntax
var methodSort = employees
    .OrderBy(e => e.Department)
    .ThenByDescending(e => e.Salary);

Console.WriteLine("Using Query Syntax:");
foreach (var emp in querySort)
{
    Console.WriteLine($"{emp.Department}: {emp.Name} - ${emp.Salary}");
}

Console.WriteLine("\nUsing Method Syntax:");
foreach (var emp in methodSort)
{
    Console.WriteLine($"{emp.Department}: {emp.Name} - ${emp.Salary}");
}

// Both produce identical output:
// Engineering: Carol - $105000
// Engineering: Alice - $95000
// Sales: David - $80000
// Sales: Bob - $75000

record Employee
{
    public string Name { get; init; }
    public string Department { get; init; }
    public decimal Salary { get; init; }
}

Notice that query syntax uses commas between sort criteria, while method syntax uses ThenBy/ThenByDescending to add secondary sorts. Both approaches produce exactly the same results and performance. Pick the style that fits your team's coding standards.

Gotchas and Solutions

Null values cause runtime errors: If your sort property might be null (like nullable DateTime or string), LINQ throws NullReferenceException when comparing. Fix this by filtering nulls first with where, or provide a default value in your orderby expression like orderby (p.ReleaseDate ?? DateTime.MinValue). This treats nulls as the earliest date, keeping your query safe.

Case-sensitive string sorting surprises users: By default, orderby uses case-sensitive comparison, so "Zebra" comes before "apple" because uppercase letters sort before lowercase in ASCII. Users expect alphabetical order regardless of case. Fix this with orderby p.Name.ToLower() or use StringComparer.OrdinalIgnoreCase with method syntax: OrderBy(p => p.Name, StringComparer.OrdinalIgnoreCase). This gives natural alphabetical sorting.

Forgetting ThenBy for multi-level method syntax: Calling OrderBy twice instead of ThenBy discards the first sort. items.OrderBy(x => x.A).OrderBy(x => x.B) only sorts by B, not A then B. Always use OrderBy for the first criterion and ThenBy for subsequent ones. Query syntax handles this automatically when you use commas.

Try It Yourself

Build a quick console app that sorts a collection by multiple criteria. This demonstrates both ascending and descending sorts with query syntax.

Steps

  1. Create a new console project: dotnet new console -n SortDemo
  2. Navigate to the project: cd SortDemo
  3. Replace the contents of Program.cs with the code below
  4. Update your .csproj file as shown
  5. Run it: dotnet run
SortDemo.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>
Program.cs
var books = new List<Book>
{
    new() { Title = "1984", Author = "Orwell", Year = 1949, Rating = 4.5 },
    new() { Title = "Brave New World", Author = "Huxley", Year = 1932, Rating = 4.2 },
    new() { Title = "Fahrenheit 451", Author = "Bradbury", Year = 1953, Rating = 4.3 },
    new() { Title = "The Handmaid's Tale", Author = "Atwood", Year = 1985, Rating = 4.6 },
    new() { Title = "Animal Farm", Author = "Orwell", Year = 1945, Rating = 4.4 }
};

// Sort by author (A-Z), then by rating (highest first)
var sorted = from b in books
             orderby b.Author ascending, b.Rating descending
             select b;

Console.WriteLine("Books by Author (Best Rated First):");
foreach (var book in sorted)
{
    Console.WriteLine($"{book.Author}: {book.Title} ({book.Year}) - {book.Rating}★");
}

record Book
{
    public string Title { get; init; }
    public string Author { get; init; }
    public int Year { get; init; }
    public double Rating { get; init; }
}

What you'll see

Books by Author (Best Rated First):
Atwood: The Handmaid's Tale (1985) - 4.6★
Bradbury: Fahrenheit 451 (1953) - 4.3★
Huxley: Brave New World (1932) - 4.2★
Orwell: 1984 (1949) - 4.5★
Orwell: Animal Farm (1945) - 4.4★

Common Questions

When should I use orderby ascending vs OrderBy method syntax?

Use query syntax (orderby) when you're already writing a LINQ query with from/where clauses for consistency. Use method syntax (OrderBy) for simple sorting or when chaining with other LINQ methods. Both compile to the same code, so choose what reads better for your team.

What's the performance difference between orderby and manual sorting?

LINQ's orderby uses optimized sorting algorithms (typically QuickSort or TimSort) that perform comparably to manual sorting. For in-memory collections, the difference is negligible. Database queries benefit because orderby translates to SQL ORDER BY, letting the database optimize. Avoid sorting millions of items in memory without pagination.

How do I sort by multiple properties with different directions?

Use orderby for the first property, then add additional sorting with ascending or descending keywords separated by commas. For example: orderby p.Category ascending, p.Price descending. This sorts first by category (A-Z), then within each category by price (high to low). Each sort level respects the previous ones.

Back to Articles