![]() |
VOOZH | about |
dotnet add package Pandatech.EFCore.PostgresExtensions --version 7.0.0
NuGet\Install-Package Pandatech.EFCore.PostgresExtensions -Version 7.0.0
<PackageReference Include="Pandatech.EFCore.PostgresExtensions" Version="7.0.0" />
<PackageVersion Include="Pandatech.EFCore.PostgresExtensions" Version="7.0.0" />Directory.Packages.props
<PackageReference Include="Pandatech.EFCore.PostgresExtensions" />Project file
paket add Pandatech.EFCore.PostgresExtensions --version 7.0.0
#r "nuget: Pandatech.EFCore.PostgresExtensions, 7.0.0"
#:package Pandatech.EFCore.PostgresExtensions@7.0.0
#addin nuget:?package=Pandatech.EFCore.PostgresExtensions&version=7.0.0Install as a Cake Addin
#tool nuget:?package=Pandatech.EFCore.PostgresExtensions&version=7.0.0Install as a Cake Tool
PostgreSQL-specific extensions for Entity Framework Core that fill the gaps left by the official Npgsql provider.
| Feature | What it does |
|---|---|
| Row-level locking | FOR UPDATE with Wait, SkipLocked, and NoWait behaviors |
| Random-increment IDs | Non-predictable sequential IDs backed by a PostgreSQL sequence |
| Natural sort keys | Human-friendly ordering for strings containing numbers |
| Schema rollback helpers | Clean Down() migration methods for all of the above |
Targets net8.0, net9.0, and net10.0.
dotnet add package Pandatech.EFCore.PostgresExtensions
PostgreSQL's FOR UPDATE clause lets you lock selected rows for the duration of a transaction. This package exposes it
as a LINQ extension method with three lock behaviors.
Register the query interceptor on your DbContext:
services.AddDbContextPool<AppDbContext>(options =>
options.UseNpgsql(connectionString)
.UseQueryLocks());
The ForUpdate method must be called inside a transaction:
await using var transaction = await dbContext.Database.BeginTransactionAsync();
var order = await dbContext.Orders
.Where(o => o.Id == orderId)
.ForUpdate(LockBehavior.SkipLocked)
.FirstOrDefaultAsync();
// Modify the locked row
order.Status = OrderStatus.Processing;
await dbContext.SaveChangesAsync();
await transaction.CommitAsync();
| Behavior | SQL generated | When to use |
|---|---|---|
Default (Wait) |
FOR UPDATE |
You need the row and can wait for it |
SkipLocked |
FOR UPDATE SKIP LOCKED |
Queue-style processing — skip rows another worker already holds |
NoWait |
FOR UPDATE NOWAIT |
Fail immediately if the row is locked |
Generates bigint IDs that increment by a random amount within a configurable range. The IDs are unique and always
increasing, but the gaps between them are unpredictable — preventing enumeration attacks while keeping an index-friendly
insert order.
public class Animal
{
public long Id { get; set; }
public string Name { get; set; }
}
public class AnimalConfiguration : IEntityTypeConfiguration<Animal>
{
public void Configure(EntityTypeBuilder<Animal> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.HasRandomIdSequence();
}
}
The function must be created before the table that references it. Add it manually at the top of your Up() method:
protected override void Up(MigrationBuilder migrationBuilder)
{
// Create the sequence + function first
migrationBuilder.CreateRandomIdSequence(
tableName: "animal",
pkName: "id",
startValue: 5,
minRandIncrementValue: 5,
maxRandIncrementValue: 10);
// Then create the table (the default value references the function)
migrationBuilder.CreateTable(...);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropRandomIdSequence("animal", "id");
migrationBuilder.DropTable("animal");
}
Parameters: startValue is the first ID returned, minRandIncrementValue and maxRandIncrementValue define the random
gap range between consecutive IDs.
Sorts strings that contain numbers the way a human would expect: "Item 2" before "Item 10", not after it. The
package creates a PostgreSQL function that zero-pads numeric substrings, producing a sortable text key.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateNaturalSortKeyFunction();
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropNaturalSortKeyFunction();
}
public class Building
{
public long Id { get; set; }
public string Address { get; set; }
public string AddressNaturalSortKey { get; set; }
}
public class BuildingConfiguration : IEntityTypeConfiguration<Building>
{
public void Configure(EntityTypeBuilder<Building> builder)
{
builder.Property(x => x.AddressNaturalSortKey)
.HasNaturalSortKey("address");
}
}
Then order by the computed column:
var sorted = await dbContext.Buildings
.OrderBy(b => b.AddressNaturalSortKey)
.ToListAsync();
For columns that store encrypted data where only the first 64 characters are deterministic (e.g., hash prefix), you can create a unique index on that prefix:
// In migration Up()
migrationBuilder.CreateUniqueIndexOnEncryptedColumn("users", "email_encrypted");
// With an optional WHERE condition
migrationBuilder.CreateUniqueIndexOnEncryptedColumn("users", "email_encrypted", "is_active = true");
// In migration Down()
migrationBuilder.DropUniqueIndexOnEncryptedColumn("users", "email_encrypted");
| Method | Target | Description |
|---|---|---|
UseQueryLocks() |
DbContextOptionsBuilder |
Registers the interceptor that rewrites tagged queries into FOR UPDATE SQL |
ForUpdate() |
IQueryable<T> |
Tags a query for row-level locking (must be inside a transaction) |
HasRandomIdSequence() |
PropertyBuilder |
Configures the property to use a random-increment sequence as its default value |
HasNaturalSortKey(column) |
PropertyBuilder |
Configures the property as a stored computed column using the natural sort function |
CreateRandomIdSequence(...) |
MigrationBuilder |
Creates the PostgreSQL sequence and generator function |
DropRandomIdSequence(...) |
MigrationBuilder |
Drops the sequence and generator function |
CreateNaturalSortKeyFunction() |
MigrationBuilder |
Creates the natural sort key function |
DropNaturalSortKeyFunction() |
MigrationBuilder |
Drops the natural sort key function |
CreateUniqueIndexOnEncryptedColumn(...) |
MigrationBuilder |
Creates a unique index on the first 64 chars of a column |
DropUniqueIndexOnEncryptedColumn(...) |
MigrationBuilder |
Drops the encrypted-column unique index |
LockBehavior: Default (wait), SkipLocked, NoWait.
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 2 NuGet packages that depend on Pandatech.EFCore.PostgresExtensions:
| Package | Downloads |
|---|---|
|
Pandatech.SharedKernel.Postgres
PostgreSQL integration helpers for ASP.NET Core 10: DbContext registration with or without pooling and audit trail, migrations, health checks, snake_case naming, query locks, exception mapping, and bulk extensions. |
|
|
Pandatech.MassTransit.PostgresOutbox
Outbox and Inbox pattern implementation for MassTransit with PostgreSQL and EF Core. Supports multiple DbContexts for modular monolith and microservice architectures. Uses PostgreSQL FOR UPDATE SKIP LOCKED for concurrency control. |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 7.0.0 | 4,233 | 2/28/2026 |
| 6.2.0 | 782 | 2/21/2026 |
| 6.1.0 | 3,057 | 1/26/2026 |
| 6.0.0 | 755 | 12/28/2025 |
| 5.1.3 | 19,193 | 8/7/2025 |
| 5.1.2 | 395 | 6/1/2025 |
| 5.1.1 | 267 | 5/6/2025 |
| 5.1.0 | 367 | 3/31/2025 |
| 5.0.0 | 253 | 3/31/2025 |
| 4.0.2 | 383 | 3/12/2025 |
| 4.0.1 | 692 | 2/17/2025 |
| 4.0.0 | 2,081 | 11/21/2024 |
| 3.0.0 | 25,367 | 7/17/2024 |
| 2.0.1 | 562 | 5/26/2024 |
| 2.0.0 | 223 | 4/10/2024 |
| 1.0.0 | 404 | 3/29/2024 |
Multi-TFM support (net8/9/10), improved documentation