Skip to content

[Mocha] Add DbContext.Outbox for atomic SaveChanges publishes - #10535

Draft
tobias-tengler wants to merge 8 commits into
mainfrom
tte/outbox-unit-of-work
Draft

tobias-tengler wants to merge 8 commits into
mainfrom
tte/outbox-unit-of-work

Conversation

@tobias-tengler

@tobias-tengler tobias-tengler commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

API endpoints and background services that rely on EF Core saves currently need to manage an explicit transaction to persist business changes and outbox messages together. Publishing separately can allow a message to escape a failed or omitted save.

This PR adds await db.Outbox.PublishAsync(message, cancellationToken) to prepare messages for the DbContext's next save. DI-resolved contexts use the current application's or consumer's scoped bus, including when contexts are pooled, while factory registrations retain their configured lifetimes. Contexts created directly through a factory are not bound for this API. It handles message cleanup, save retries, synchronous saves after preparation, and asynchronous save interceptors; scheduled messages are rejected. It also prevents application clock skew from delaying new outbox rows and updates the reliability documentation and regression tests. Validation passed: 47 targeted PostgreSQL tests, the test project build with no warnings or errors, and documentation formatting.

@github-actions github-actions Bot added 📚 documentation This issue is about working on our documentation. 🌶️ mocha labels Oct 13, 2026
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Patch coverage

96.5% of changed lines covered (718/744)

File Covered Changed Patch %
src/Mocha/src/Mocha/Endpoints/DispatchEndpoint.cs 37 49 75.5% 🔴
src/Mocha/src/Mocha.EntityFrameworkCore/DbContextAccessor.cs 31 36 86.1% 🟡
…/Mocha.EntityFrameworkCore/MessageBusHostBuilderExtensions.cs 9 10 90.0% 🟡
…/src/Mocha.EntityFrameworkCore/RegistrationThrowHelper.cs 10 11 90.9% 🟡
src/Mocha/src/Mocha.EntityFrameworkCore/ThrowHelper.cs 12 13 92.3% 🟡
…/Outbox/OutboxServiceCollectionExtensions.cs 102 106 96.2% 🟡
…/src/Mocha.EntityFrameworkCore/Outbox/DbContextOutbox.cs 84 86 97.7% 🟡
…/Generators/MessagingDependencyInjectionGenerator.cs 4 4 100.0% 🟢
…/Mocha.Analyzers/Inspectors/CallSiteMessageTypeInspector.cs 4 4 100.0% 🟢
src/Mocha/src/Mocha.Analyzers/KnownTypeSymbols.cs 1 1 100.0% 🟢
src/Mocha/src/Mocha.Analyzers/MessagingGenerator.cs 2 2 100.0% 🟢
…/Mocha.EntityFrameworkCore.Postgres/Outbox/OutboxMessage.cs 3 3 100.0% 🟢
…/Mocha.EntityFrameworkCore.Postgres/Outbox/OutboxProcessor.cs 19 19 100.0% 🟢
…/Outbox/PostgresDbContextOutboxProvider.cs 39 39 100.0% 🟢
…/Outbox/PostgresMessageOutbox.cs 16 16 100.0% 🟢
…/Outbox/PostgresMessageOutboxOptions.cs 1 1 100.0% 🟢
…/Outbox/PostgresMessageOutboxQueries.cs 5 5 100.0% 🟢
…/Outbox/PostgresOutboxTracker.cs 59 59 100.0% 🟢
…/Mocha/src/Mocha.EntityFrameworkCore.Postgres/ThrowHelper.cs 11 11 100.0% 🟢
…/Outbox/DbContextOutboxExtensions.cs 7 7 100.0% 🟢

+16 more changed files; see the JSON below for uncovered lines.

Uncovered changed lines (JSON)
{
  "sha": "3d292c0dedaabb45f11599a1eb81428f27cfafd1",
  "files": [
    { "path": "src/Mocha/src/Mocha/Endpoints/DispatchEndpoint.cs", "ranges": [[74, 74], [78, 81], [83, 86], [187, 189]] },
    { "path": "src/Mocha/src/Mocha.EntityFrameworkCore/DbContextAccessor.cs", "ranges": [[33, 36], [40, 40]] },
    { "path": "src/Mocha/src/Mocha.EntityFrameworkCore/MessageBusHostBuilderExtensions.cs", "ranges": [[104, 104]] },
    { "path": "src/Mocha/src/Mocha.EntityFrameworkCore/RegistrationThrowHelper.cs", "ranges": [[22, 22]] },
    { "path": "src/Mocha/src/Mocha.EntityFrameworkCore/ThrowHelper.cs", "ranges": [[22, 22]] },
    { "path": "src/Mocha/src/Mocha.EntityFrameworkCore.Postgres/Outbox/OutboxServiceCollectionExtensions.cs", "ranges": [[111, 112], [129, 130]] },
    { "path": "src/Mocha/src/Mocha.EntityFrameworkCore/Outbox/DbContextOutbox.cs", "ranges": [[133, 134]] }
  ]
}

Project coverage: 59.2% (314191/530331 lines)

@tobias-tengler tobias-tengler changed the title [Mocha] Write outbox messages with SaveChangesAsync outside a transaction [Mocha] Require a transaction for outbox publishes and add DbContext.Outbox Oct 16, 2026
@tobias-tengler
tobias-tengler force-pushed the tte/outbox-unit-of-work branch from 9403cbd to 6409ccc Compare October 16, 2026 08:54
@tobias-tengler tobias-tengler changed the title [Mocha] Require a transaction for outbox publishes and add DbContext.Outbox [Mocha] Add DbContext.Outbox for atomic SaveChanges publishes Oct 16, 2026
@tobias-tengler
tobias-tengler force-pushed the tte/outbox-unit-of-work branch 4 times, most recently from 06228cc to 987ad6b Compare October 19, 2026 07:18
@tobias-tengler
tobias-tengler changed the base branch from main to tte/outbox-require-transaction October 19, 2026 07:18
@tobias-tengler
tobias-tengler added this pull request to stack #10637 October 19, 2026 07:26
Base automatically changed from tte/outbox-require-transaction to main October 19, 2026 19:31
@tobias-tengler
tobias-tengler force-pushed the tte/outbox-unit-of-work branch 2 times, most recently from 632f33a to c93557a Compare October 21, 2026 09:38
@tobias-tengler
tobias-tengler force-pushed the tte/outbox-unit-of-work branch from ce75cfa to 0eb8145 Compare October 21, 2026 14:49
@tobias-tengler
tobias-tengler force-pushed the tte/outbox-unit-of-work branch from 0eb8145 to 3f58253 Compare October 21, 2026 15:41

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📚 documentation This issue is about working on our documentation. 🌶️ mocha

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant