Skip to content

feat: IIdempotencyStore – EF Core implementation #226

@samtrion

Description

@samtrion

User Story

As a developer using EF Core with Pulse, I want a fully functional IIdempotencyStore backed by Entity Framework Core, so that I can prevent duplicate command execution using my existing DbContext without adopting a separate ADO.NET provider.


Background

IIdempotencyStore and IdempotencyCommandInterceptor are implemented, but no concrete store exists. Without a registered IIdempotencyStore, the interceptor is a no-op and idempotency is not enforced. An EF Core implementation integrates naturally with applications that already use EF for persistence, sharing the same DbContext transaction scope.


Requirements

  • Define IdempotencyKeySchema constants in NetEvolve.Pulse.Extensibility (schema name, table name, column names, max lengths).
  • Define IIdempotencyStoreDbContext in NetEvolve.Pulse.EntityFramework: DbSet<IdempotencyKey> IdempotencyKeys { get; }.
  • Create IdempotencyKey POCO in NetEvolve.Pulse.EntityFramework: string IdempotencyKey (PK), DateTimeOffset CreatedAt.
  • Create IdempotencyKeyOptions in NetEvolve.Pulse.EntityFramework: Schema, TableName, TimeToLive?.
  • Create abstract IdempotencyKeyConfigurationBase : IEntityTypeConfiguration<IdempotencyKey> with provider-overridable column types.
  • Create provider-specific configurations: SqlServerIdempotencyKeyConfiguration, PostgreSqlIdempotencyKeyConfiguration, SqliteIdempotencyKeyConfiguration.
  • Implement EntityFrameworkIdempotencyStore<TContext> : IIdempotencyStore:
    • ExistsAsync: compiled query AnyAsync(k => k.IdempotencyKey == key), respecting TimeToLive when set.
    • StoreAsync: insert new IdempotencyKey; handle duplicate-key exception as idempotent (return without throwing).
  • Expose AddEntityFrameworkIdempotencyStore<TContext>(this IMediatorBuilder, Action<IdempotencyKeyOptions>?) extension method.

Acceptance Criteria

  • IdempotencyKeySchema constants match the schema expected by all provider configurations.
  • EntityFrameworkIdempotencyStore.ExistsAsync returns true for an existing key and false for a new one.
  • When TimeToLive is set, expired keys are treated as absent.
  • StoreAsync does not throw when a duplicate key is inserted concurrently.
  • AddEntityFrameworkIdempotencyStore<TContext>() registers IIdempotencyStore as Scoped.
  • Unit tests cover: key exists, key absent, TTL expiry, duplicate insert, options wiring.

Out of Scope

  • ADO.NET implementations (tracked per-provider in separate issues).
  • TTL-based cleanup jobs.

Metadata

Metadata

Labels

type:featureIndicates a new feature or enhancement to be added.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions