LiteBus
CatalogStorage

Outbox EF Core Adapter

  • ID: storage.outbox.adapter.efcore
  • Name: Outbox EF Core adapter
  • Maturity: GA
  • Summary: Entity Framework Core outbox store with factory-created operation contexts and transactional enqueue through a typed writer, existing context, or SaveChanges interceptor.

What It Does

EfCoreOutboxStore implements all outbox roles on DbSet<OutboxMessageEntity>. Registration:

outbox.UseEntityFrameworkCoreStorage(o => o.UseDbContext<AppDbContext>());

Default auto-commit operations create and dispose a context through IDbContextFactory<TContext>. Caller-owned transactional patterns are documented in Outbox EF Core storage and storage.transactional.writes.

Supports PostgreSQL, SQL Server, MySQL, SQLite, and in-memory EF providers for leasing. MySQL uses a chronological index with skip-locked READ COMMITTED transactions. SQLite uses UTC-tick timestamp columns and serializable file transactions.

Public Surface

Consumer Contracts

TypeRole
EfCoreOutboxStoreAll outbox store roles on DbSet<OutboxMessageEntity>
ITransactionalOutbox<TContext>Typed transactional enqueue
LiteBusOutboxSaveChangesInterceptorStage enqueues until SaveChanges
OutboxMessageEntityEF persistence entity
IOutboxDbContextDbContext marker interface

Invocation

MethodTypical caller
IOutboxStore.EnqueueAsyncAuto-commit operation context (calls SaveChanges)
ITransactionalOutbox<TContext>.EnqueueAsyncCommand handlers in EF transaction
EfCoreOutboxStore.UseExistingDbContextManual staging on caller context
IOutboxLeaseStore.LeasePendingAsyncOutbox processor

Registration

outbox.UseEntityFrameworkCoreStorage(o =>
{
    o.UseDbContext<AppDbContext>();
    o.EnableSaveChangesInterceptor();
});
  • Interceptor must be registered on both LiteBus module and DbContextOptions.

Configuration

Option surfacePurpose
EntityFrameworkCoreOutboxStoreOptionsSchema, table, lease duration
UseDbContext<TContext>()Selects the application-registered IDbContextFactory<TContext>
EnableSaveChangesInterceptor() / EnforceTransactionalSetup()Transactional enqueue
GetModelBuilderConfiguration()Application-owned EF model

Extension Points

  • Supported relational providers via EfCoreStorageProvider.
  • UseExistingDbContext for read/write split or manual unit-of-work patterns.

Packages

PackageDependency role
LiteBus.Outbox.Storage.EntityFrameworkCoreFeature bridge
LiteBus.Storage.EntityFrameworkCoreTechnology adapter (transitive)

Requires

  • IOutboxDbContext on application DbContext
  • AddDbContextFactory<TContext>(...) in application composition
  • Application-owned EF migrations
  • Outbox dispatch before processor

Invariants

  • Schema and table names in options must match OnModelCreating configuration.
  • Interceptor pattern requires registration on both LiteBus module and DbContext options.

Non-Goals

  • LiteBus-driven EF migrations
  • Broker publish

Observability

MeterInstrumentTypeWhen observedTagsStorage coupling
LiteBus.Outboxlitebus.outbox.queue.depthObservable gaugeOpenTelemetry scrapelitebus.outbox.statusEF diagnostics role
LiteBus.Outboxlitebus.outbox.processor.stateObservable gaugeProcessor loopState enumProcessor on EF store
LiteBus.Outboxlitebus.outbox.processor.leases_acquiredCounterProcessor pass:EF lease SQL
LiteBus.Outboxlitebus.outbox.processor.dispatch_durationHistogramAfter publication:Dispatch duration
LiteBus.Outboxlitebus.outbox.processor.lease_lostCounterLease renewal failure:Long publication
LiteBus.Outboxlitebus.outbox.processor.persist_skippedCounterSkipped persist:Lost lease
LiteBus.Outboxlitebus.outbox.processor.persist_rejectedCounterRejected persist:Competing workers
LiteBus.Outboxlitebus.outbox.cleanup.errorsCounterRetention failure:EF retention store

Register through AddLiteBusOutboxMetrics(). Constants on LiteBusOutboxTelemetry.

Deep Docs

Test Coverage

Covered

EfCoreOutboxStorageModuleTests.AddEfCoreOutboxStorage_ShouldRegisterSingleStoreInstanceForAllRoles

  • Use case: Register single outbox store instance for all roles
  • Test kind: Unit
  • Description: Build EF outbox storage module
  • Behavior: Resolve all outbox store interfaces
  • Expected outcome: Same instance for every role
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.UnitTests

EfCoreOutboxStorageModuleTests.AddEfCoreOutboxStorage_WithEnforceTransactionalSetupWithoutInterceptor_ShouldThrowOnBuild

  • Use case: Throw when transactional setup enforced without interceptor
  • Test kind: Unit
  • Description: Enforce without interceptor
  • Behavior: Module build
  • Expected outcome: Configuration exception
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.UnitTests

EfCoreOutboxStoreContractTests (Inherits OutboxStoreContractTests)

  • Use case: In-memory EF outbox contract suite
  • Test kind: Contract (unit)
  • Description: Full contract against EF in-memory provider
  • Behavior: Inherited contract tests
  • Expected outcome: Parity with shared contract suite
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.UnitTests

EfCoreOutboxStorePostgreSqlContractTests (Inherits OutboxStoreContractTests)

  • Use case: PostgreSQL EF outbox contract suite
  • Test kind: Contract (integration)
  • Description: Migrated PostgreSQL EF store contract tests
  • Behavior: Inherited contract tests
  • Expected outcome: Parity with shared contract suite
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.IntegrationTests

EfCoreOutboxStoreSqlServerContractTests (Inherits OutboxStoreContractTests)

  • Use case: SQL Server EF outbox contract suite
  • Test kind: Contract (integration)
  • Description: Migrated SQL Server EF store contract tests
  • Behavior: Inherited contract tests
  • Expected outcome: Parity with shared contract suite
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.IntegrationTests

EfCoreOutboxProcessorEndToEndTests.ProcessPendingAsync_ShouldPublishEventThroughEfCoreStore

  • Use case: Processor E2E publishes through EF outbox store
  • Test kind: Integration
  • Description: Enqueue and process via EF store
  • Behavior: Processor with in-process dispatcher
  • Expected outcome: Event published; row terminal
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.IntegrationTests

EfCoreOutboxFindExistingTests.EnqueueAsync_when_id_and_idempotency_key_match_different_rows_should_prefer_message_id_row

  • Use case: Prefer message id row when id and idempotency match different rows
  • Test kind: Unit
  • Description: Two rows with overlapping identifiers
  • Behavior: Enqueue resolving existing row
  • Expected outcome: Message id match wins over idempotency key
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.UnitTests

EfCoreOutboxStoreConcurrencyTests.PersistAsync_WhenTwoWorkersPersistSameEnvelopeConcurrently_ShouldApplyExactlyOnce

  • Use case: Concurrent persist applies exactly once
  • Test kind: Integration
  • Description: Two workers persist same leased envelope
  • Behavior: Concurrent PersistAsync
  • Expected outcome: Single terminal update
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.IntegrationTests

OutboxEntityFrameworkCoreModelTests.GetModelBuilderConfiguration_ShouldMapVersion1Columns

  • Use case: Map version 1 outbox columns in model
  • Test kind: Unit
  • Description: Model extension configuration
  • Behavior: Inspect EF model
  • Expected outcome: v1 column layout mapped
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.UnitTests

OutboxEntityFrameworkCoreModelTests.GetModelBuilderConfiguration_ShouldMapEntityColumns

  • Use case: Map outbox entity columns in model
  • Test kind: Unit
  • Description: Property and column mappings
  • Behavior: Model builder extension
  • Expected outcome: Topic, payload, scheduling columns mapped
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.UnitTests

EfCoreOutboxSaveChangesInterceptorIntegrationTests.SaveChangesAsync_ShouldPersistOutboxMessage_WhenTransactionCommits

  • Use case: Interceptor persists outbox on commit
  • Test kind: Integration
  • Description: Enqueue via interceptor and commit
  • Behavior: Successful SaveChanges and commit
  • Expected outcome: Outbox row in database
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.IntegrationTests

TransactionalOutboxEnqueueTests.EnqueueAsync_should_stage_envelope_with_contract_and_metadata

  • Use case: Transactional enqueue stages envelope with metadata
  • Test kind: Unit
  • Description: ITransactionalOutbox before save
  • Behavior: Enqueue then inspect staged state
  • Expected outcome: Contract and metadata staged
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.UnitTests

EfCoreOutboxTransactionalIntegrationTests.UseExistingDbContext_ShouldCommitDomainAndOutboxTogether

  • Use case: EF use-existing DbContext commits outbox with domain
  • Test kind: Integration
  • Description: Shared DbContext transaction
  • Behavior: SaveChanges and commit
  • Expected outcome: Domain and outbox rows committed
  • Remarks: LiteBus.Outbox.Storage.EntityFrameworkCore.IntegrationTests

EfCoreOutboxStoreMySqlContractTests (Inherits OutboxStoreContractTests)

  • Use case: MySQL EF outbox contract parity
  • Test kind: Contract (integration)
  • Description: Full outbox contract suite against MySQL 8.4 through Pomelo
  • Behavior: Ordered concurrent leasing, append outcomes, fencing, queries, and purge
  • Expected outcome: Same outcomes as the shared contract suite
  • Remarks: LiteBus.Storage.IntegrationTests

EfCoreOutboxStoreSqliteContractTests (Inherits OutboxStoreContractTests)

  • Use case: File-backed SQLite outbox contract parity
  • Test kind: Contract (integration)
  • Description: Full outbox contract suite against independent contexts sharing one database file
  • Behavior: Serializable claims, UTC-tick timestamp queries, append outcomes, and fencing
  • Expected outcome: Same outcomes as the shared contract suite
  • Remarks: LiteBus.Storage.IntegrationTests

Untested

  • Read/write split DbContext with explicit UseExistingDbContext integration test.

Out-of-Scope

  • LiteBus-driven EF migrations.
  • Broker publish (dispatch axis).

On this page

Outbox EF Core AdapterWhat It DoesPublic SurfaceConsumer ContractsInvocationRegistrationConfigurationExtension PointsPackagesRequiresInvariantsNon-GoalsObservabilityDeep DocsTest CoverageCoveredEfCoreOutboxStorageModuleTests.AddEfCoreOutboxStorage_ShouldRegisterSingleStoreInstanceForAllRolesEfCoreOutboxStorageModuleTests.AddEfCoreOutboxStorage_WithEnforceTransactionalSetupWithoutInterceptor_ShouldThrowOnBuildEfCoreOutboxStoreContractTests (Inherits OutboxStoreContractTests)EfCoreOutboxStorePostgreSqlContractTests (Inherits OutboxStoreContractTests)EfCoreOutboxStoreSqlServerContractTests (Inherits OutboxStoreContractTests)EfCoreOutboxProcessorEndToEndTests.ProcessPendingAsync_ShouldPublishEventThroughEfCoreStoreEfCoreOutboxFindExistingTests.EnqueueAsync_when_id_and_idempotency_key_match_different_rows_should_prefer_message_id_rowEfCoreOutboxStoreConcurrencyTests.PersistAsync_WhenTwoWorkersPersistSameEnvelopeConcurrently_ShouldApplyExactlyOnceOutboxEntityFrameworkCoreModelTests.GetModelBuilderConfiguration_ShouldMapVersion1ColumnsOutboxEntityFrameworkCoreModelTests.GetModelBuilderConfiguration_ShouldMapEntityColumnsEfCoreOutboxSaveChangesInterceptorIntegrationTests.SaveChangesAsync_ShouldPersistOutboxMessage_WhenTransactionCommitsTransactionalOutboxEnqueueTests.EnqueueAsync_should_stage_envelope_with_contract_and_metadataEfCoreOutboxTransactionalIntegrationTests.UseExistingDbContext_ShouldCommitDomainAndOutboxTogetherEfCoreOutboxStoreMySqlContractTests (Inherits OutboxStoreContractTests)EfCoreOutboxStoreSqliteContractTests (Inherits OutboxStoreContractTests)UntestedOut-of-Scope