Overview

Case-Insensitive Sort in .NET-PostgreSQL Application

Introduction

When migrating a .NET application from Microsoft SQL Server (MSSQL) to PostgreSQL, one of the most common surprises is a change in string sorting behavior. MSSQL typically uses case-insensitive collations by default, while PostgreSQL treats text comparisons as case-sensitive unless configured otherwise.

This difference can cause unexpected ordering results, failing tests, and inconsistent user-facing sorting after migration.

This article explains why the behavior differs and presents two practical solutions: handling case-insensitive sorting in .NET using StringComparer.OrdinalIgnoreCase, or configuring PostgreSQL to behave similarly using the citext extension.


The Problem: Different Default Collations

Consider the following table:

ordername
1b
2B
3a
4a1
5a11
6a2
7a20
8A
9a19

Running the query below in PostgreSQL:

SELECT name FROM testsorting ORDER BY name;

PostgreSQL Result

A
B
a
a1
a11
a19
a2
a20
b

At first glance, this may look incorrect. Many developers expect a case-insensitive order such as a, A, b, B. However, PostgreSQL sorts text based on binary comparison by default, where uppercase letters come before lowercase letters.


Why PostgreSQL Sorts Uppercase Before Lowercase

PostgreSQL follows standard ASCII/Unicode ordering, where:

'A' (65) < 'a' (97)

Because of this, all uppercase letters are sorted before lowercase letters unless a case-insensitive collation or data type is used.

In contrast, MSSQL commonly uses collations such as SQL_Latin1_General_CP1_CI_AS, where CI stands for case-insensitive, resulting in a different default sorting behavior.


Resolution Options

There are two main ways to address this issue depending on where you want to enforce case insensitivity: in the application layer or the database layer.


Option 1: Case-Insensitive Sorting in .NET Using StringComparer

If sorting is performed in memory or your codebase is small, you can apply case-insensitive ordering directly in LINQ using StringComparer.OrdinalIgnoreCase.

var listOfString = new List<string>
{
"A", "a", "b", "B",
};
var orderedListOfString = listOfString
.OrderBy(name => name, StringComparer.OrdinalIgnoreCase)
.ToList();

Result

A
a
b
B

Advantages

  • No database schema changes required
  • Database remains provider-agnostic
  • Easy to implement in small codebases

Limitations

  • Requires updating multiple LINQ queries
  • Does not affect SQL-level ordering, indexes, or constraints
  • Inefficient for large datasets retrieved from the database

Option 2: Using PostgreSQL citext for Case-Insensitive Behavior

For applications originally designed around MSSQL’s case-insensitive behavior, modifying application code everywhere may be impractical. PostgreSQL provides the citext extension to support case-insensitive comparisons at the database level.

The citext (case-insensitive text) type behaves like text but compares values without considering case. This means queries, indexes, and constraints all become case-insensitive automatically.


Enabling citext in Entity Framework Core

To enable the extension when using EF Core:

protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.HasPostgresExtension("citext");
}

After enabling the extension, you can alter an existing column:

ALTER TABLE testsorting
ALTER COLUMN name TYPE citext;

Once the column uses citext, PostgreSQL performs case-insensitive ordering:

SELECT name FROM testsorting ORDER BY name;

Result

a
A
a1
a11
a19
a2
a20
b
B

This behavior closely matches MSSQL’s default sorting and ensures consistent results across database queries.


Choosing the Right Approach

Use StringComparer.OrdinalIgnoreCase when:

  • Sorting is done in memory
  • You want to keep the database fully portable
  • Only a few queries require case-insensitive ordering

Use citext when:

  • Migrating from MSSQL and expecting the same behavior
  • Case-insensitive comparisons are required in SQL queries
  • You need case-insensitive indexes and unique constraints
  • You want to minimize changes to existing application code

Conclusion

The difference in default case sensitivity between MSSQL and PostgreSQL is a common source of confusion during database migrations. PostgreSQL’s strict case-sensitive ordering is technically correct but often unexpected for developers coming from MSSQL.

By either applying case-insensitive sorting in .NET using StringComparer.OrdinalIgnoreCase or enabling PostgreSQL’s citext extension, you can restore predictable and consistent sorting behavior across your application.

For most real-world migrations, using citext provides the cleanest and most maintainable solution because it preserves expected behavior without requiring widespread code changes.