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
Out of Scope
- ADO.NET implementations (tracked per-provider in separate issues).
- TTL-based cleanup jobs.
User Story
As a developer using EF Core with Pulse, I want a fully functional
IIdempotencyStorebacked by Entity Framework Core, so that I can prevent duplicate command execution using my existingDbContextwithout adopting a separate ADO.NET provider.Background
IIdempotencyStoreandIdempotencyCommandInterceptorare implemented, but no concrete store exists. Without a registeredIIdempotencyStore, 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 sameDbContexttransaction scope.Requirements
IdempotencyKeySchemaconstants inNetEvolve.Pulse.Extensibility(schema name, table name, column names, max lengths).IIdempotencyStoreDbContextinNetEvolve.Pulse.EntityFramework:DbSet<IdempotencyKey> IdempotencyKeys { get; }.IdempotencyKeyPOCO inNetEvolve.Pulse.EntityFramework:string IdempotencyKey(PK),DateTimeOffset CreatedAt.IdempotencyKeyOptionsinNetEvolve.Pulse.EntityFramework:Schema,TableName,TimeToLive?.IdempotencyKeyConfigurationBase : IEntityTypeConfiguration<IdempotencyKey>with provider-overridable column types.SqlServerIdempotencyKeyConfiguration,PostgreSqlIdempotencyKeyConfiguration,SqliteIdempotencyKeyConfiguration.EntityFrameworkIdempotencyStore<TContext> : IIdempotencyStore:ExistsAsync: compiled queryAnyAsync(k => k.IdempotencyKey == key), respectingTimeToLivewhen set.StoreAsync: insert newIdempotencyKey; handle duplicate-key exception as idempotent (return without throwing).AddEntityFrameworkIdempotencyStore<TContext>(this IMediatorBuilder, Action<IdempotencyKeyOptions>?)extension method.Acceptance Criteria
IdempotencyKeySchemaconstants match the schema expected by all provider configurations.EntityFrameworkIdempotencyStore.ExistsAsyncreturnstruefor an existing key andfalsefor a new one.TimeToLiveis set, expired keys are treated as absent.StoreAsyncdoes not throw when a duplicate key is inserted concurrently.AddEntityFrameworkIdempotencyStore<TContext>()registersIIdempotencyStoreasScoped.Out of Scope