DKNet.AspCore.Idempotency.NpgsqlStore
10.1.19
dotnet add package DKNet.AspCore.Idempotency.NpgsqlStore --version 10.1.19
NuGet\Install-Package DKNet.AspCore.Idempotency.NpgsqlStore -Version 10.1.19
<PackageReference Include="DKNet.AspCore.Idempotency.NpgsqlStore" Version="10.1.19" />
<PackageVersion Include="DKNet.AspCore.Idempotency.NpgsqlStore" Version="10.1.19" />
<PackageReference Include="DKNet.AspCore.Idempotency.NpgsqlStore" />
paket add DKNet.AspCore.Idempotency.NpgsqlStore --version 10.1.19
#r "nuget: DKNet.AspCore.Idempotency.NpgsqlStore, 10.1.19"
#:package DKNet.AspCore.Idempotency.NpgsqlStore@10.1.19
#addin nuget:?package=DKNet.AspCore.Idempotency.NpgsqlStore&version=10.1.19
#tool nuget:?package=DKNet.AspCore.Idempotency.NpgsqlStore&version=10.1.19
DKNet.AspCore.Idempotency.NpgsqlStore
PostgreSQL-backed persistent store for DKNet.AspCore.Idempotency, built on the shared EF Core relational
store (DKNet.AspCore.Idempotency.Relational).
✨ Why use it?
- Persistent – idempotency keys live in a Postgres
IdempotencyKeystable, surviving app restarts. - Atomic under concurrency – a unique index on the composite key serializes duplicate requests in the database itself; no application-level locking needed.
- Migrations included – ships with its own EF Core migration and applies it automatically on first use, independently per connection string.
- Multi-database ready – register the store against several Postgres databases (e.g. per tenant) from the same process; each one is prepared on its own.
- Npgsql-tuned – retry-on-failure and split-query behaviour are configured out of the box.
🚀 Quick Start
dotnet add package DKNet.AspCore.Idempotency.NpgsqlStore
using DKNet.AspCore.Idempotency;
using DKNet.AspCore.Idempotency.NpgsqlStore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddIdempotencyWithNpgsqlStore(
builder.Configuration.GetConnectionString("IdempotencyDb")!,
options =>
{
options.Expiration = TimeSpan.FromHours(48);
options.ConflictHandling = IdempotentConflictHandling.CachedResult;
});
var app = builder.Build();
app.MapPost("/orders", () => Results.Ok())
.RequiredIdempotentKey();
app.Run();
Clients send an X-Idempotency-Key header on POST /orders; a retry with the same key replays the first
response instead of creating a second order.
Customisation reference
There is no PostgreSQL-specific options type. AddIdempotencyWithNpgsqlStore configures the shared IdempotencyOptions from
DKNet.AspCore.Idempotency:
| Knob | Type | Default | Effect |
|---|---|---|---|
IdempotencyHeaderKey |
string |
"X-Idempotency-Key" |
Request header the filter reads the key from. |
IdempotencyKeyPattern |
string |
^[a-zA-Z0-9\-_]+$ |
Regex a key must match; a mismatch is 400 Bad Request. |
MaxIdempotencyKeyLength |
int |
255 |
Longer keys are rejected with 400. |
ConflictHandling |
IdempotentConflictHandling |
ConflictResponse |
ConflictResponse answers a duplicate with 409; CachedResult replays the original status, body and content type. |
Expiration |
TimeSpan |
4 hours |
Absolute lifetime of a cached result before the key is treated as new again. |
InFlightReservationTimeout |
TimeSpan |
30 seconds |
Lifetime of the in-flight reservation placeholder before it can be reclaimed. |
MinStatusCodeForCaching |
int |
200 |
Inclusive lower bound of the cacheable status range (must be ≥ 100). |
MaxStatusCodeForCaching |
int |
299 |
Inclusive upper bound (must be ≤ 599 and ≥ the minimum). |
AdditionalCacheableStatusCodes |
HashSet<int> (get-only, mutable) |
empty | Extra status codes cached outside the min/max window. |
CachePrefix |
string |
"idem" |
Prepended, unchanged, to every storage key. |
JsonSerializerOptions |
JsonSerializerOptions |
camelCase naming policy | Used to serialize and deserialize the cached response body. |
KeyScopeResolver |
Func<HttpContext, string?>? |
null |
Custom caller-scope resolver. When set it is used verbatim and the default chain is skipped. |
ScopeHmacSecret |
string? |
null |
Enables the Authorization-header HMAC-SHA256 fallback in the default scope chain. |
IncludeClientIpInScope |
bool |
false |
Enables the client-IP fallback in the default scope chain. |
Values are validated eagerly at registration: an empty header key or cache prefix, a non-positive expiration, a
status-code window outside 100–599 or with min above max, a null JsonSerializerOptions, a
MaxIdempotencyKeyLength below 1, an empty key pattern, or a whitespace ScopeHmacSecret each throw
ArgumentException immediately rather than failing at request time.
Registration entry points
| Method | Registers |
|---|---|
AddIdempotencyNpgsqlStore(connectionString) |
IdempotencyDbContext and IDbContextFactory<IdempotencyDbContext> only — not the key store, so on its own it leaves no IIdempotencyKeyStore registered. |
AddIdempotencyWithNpgsqlStore(connectionString, config) |
The above, then AddIdempotentKey<IdempotencyPostgresStore>(config). This is the call an application makes. |
Fixed by this package, not exposed as options
| Setting | Value |
|---|---|
EnableRetryOnFailure |
3 retries, 5 seconds apart |
UseQuerySplittingBehavior |
QuerySplittingBehavior.SplitQuery |
MigrationsAssembly |
this package's assembly |
MigrationsHistoryTable |
migrate.IdempotencyDbContext |
optionsLifetime |
ServiceLifetime.Singleton (the DbContext itself stays scoped) |
| Table, indexes, constraint | IdempotencyKeys with UX_CompositeKey (unique), IX_IdempotencyKeys_ExpiresAt, and CK_StatusCode_Valid (100–599) |
You provide a reachable database and a login that can create tables in it; the package creates and migrates the
table on first use. Nothing sweeps expired rows — ExpiresAt is indexed so you can add your own cleanup job.
IdempotencyPostgresStore is internal, so AddIdempotentKey<IdempotencyPostgresStore>() does not compile in
application code — AddIdempotencyWithNpgsqlStore(...) is the supported way in. Both methods are first-wins: a
second call with a different connection string is silently a no-op.
📖 Documentation
Full guide — Postgres schema, concurrency behaviour, multi-database support, and how this store composes with the core and relational packages: DKNet.AspCore.Idempotency.NpgsqlStore.md
License
MIT — see LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- DKNet.AspCore.Idempotency (>= 10.1.19)
- DKNet.AspCore.Idempotency.Relational (>= 10.1.19)
- Microsoft.EntityFrameworkCore (>= 10.0.11)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.11)
- Microsoft.Extensions.Options (>= 10.0.11)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 10.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.1.19 | 26 | 9/3/2026 |
| 10.1.18 | 28 | 9/3/2026 |
| 10.1.17 | 35 | 9/3/2026 |
| 10.1.16 | 29 | 9/3/2026 |
| 10.1.15 | 39 | 9/1/2026 |
| 10.1.14 | 35 | 9/1/2026 |
| 10.1.13 | 74 | 8/31/2026 |
| 10.1.12 | 87 | 8/25/2026 |
| 10.1.11 | 86 | 8/24/2026 |
| 10.1.10 | 97 | 8/22/2026 |
| 10.1.9 | 95 | 8/22/2026 |
| 10.1.8 | 90 | 8/21/2026 |
| 10.1.7 | 94 | 8/21/2026 |
| 10.1.6 | 91 | 8/21/2026 |
| 10.1.5 | 88 | 8/20/2026 |
| 10.1.4 | 84 | 8/20/2026 |
| 10.1.3 | 102 | 8/19/2026 |
| 10.1.2 | 91 | 8/19/2026 |
| 10.1.1 | 94 | 8/19/2026 |
| 10.0.36 | 92 | 8/18/2026 |