Skip to content

Enlist DbContext-injecting tuple-cascade HTTP endpoints in the EF Core outbox#3362

Merged
jeremydmiller merged 1 commit into
mainfrom
fix/3358-http-dbcontext-tuple-cascade
Jul 10, 2026
Merged

Enlist DbContext-injecting tuple-cascade HTTP endpoints in the EF Core outbox#3362
jeremydmiller merged 1 commit into
mainfrom
fix/3358-http-dbcontext-tuple-cascade

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #3358. Completes the GH-3291/GH-3353 defect family (follow-up to #3298 and #3357).

The three endpoint shapes

# Endpoint shape Overload that claims it Status
1 Injects IMessageBus, publishes explicitly two-arg (RequiresOutbox() true) fixed by #3298
2 Storage action return, no DbContext dependency entityType (side effects) fixed by #3357
3 Injects the DbContext, cascades via return tuple two-arg (CanApply true) this PR

Shape 3 was empirically confirmed while filing #3358: even after #3357, the generated chain showed enlist=False cascade=True requiresOutbox=False — the cascade dispatched by the send-now branch of MessageBus.PersistOrSendAsync before SaveChangesAsync committed.

Fix

Drop the chain.RequiresOutbox() gate from the GH-3291 branch of EFCorePersistenceFrameProvider.ApplyTransactionSupport(chain, container). HttpChain.RequiresOutbox() only reflects an injected IMessageBus/IMessageContext dependency — a tuple-cascading endpoint can never satisfy it. The drop is safe because of #3357's hasDatabaseBackedMessagePersistence guard:

  • Enlisting an endpoint that turns out not to cascade anything is a no-op flush at commit time.
  • Persistence-less applications keep their pre-existing send-now composition (EfCoreEnvelopeTransaction's constructor would throw without a message database) — pinned by a dedicated guard-rail test.
  • Message handlers are untouched (isHttpChain gate) and multi-tenanted chains keep their existing path.

Tests

New Bug_3358_lightweight_dbcontext_tuple_cascade (codegen-surface assertions, same conventions as the #3353 sibling), with the endpoint in the Marten-free Wolverine.Http.Tests.EfCoreOnly assembly:

  • Enlist test: chain asserts RequiresOutbox() == false (the very shape under test), then EnlistInOutboxAsync present, no standalone FlushOutgoingMessages postprocessor, and enlist → SaveChangesAsyncCommitAsync ordering. Red-first verified — fails without the gate change.
  • Guard-rail test: same endpoint with no message store keeps plain SaveChangesAsync and never enlists.

Verification

  • Full Wolverine.Http.Tests: 817 passed / 0 failed
  • EfCoreTests: 186/187 (lone failure is the documented Bug_1846 load-flake; passes isolated)
  • Full wolverine.slnx Release build: 0 warnings / 0 errors

🤖 Generated with Claude Code

…e outbox (GH-3358)

The GH-3291 branch of EFCorePersistenceFrameProvider.ApplyTransactionSupport
(chain, container) was gated on chain.RequiresOutbox(), which for an HttpChain
only reflects an injected IMessageBus/IMessageContext dependency. A Lightweight
mode HTTP endpoint that injects the DbContext and cascades purely through its
return tuple can never satisfy that gate, so its cascaded messages were still
dispatched by the send-now branch of MessageBus.PersistOrSendAsync before the
SaveChangesAsync postprocessor committed - the third and last endpoint shape in
the GH-3291/GH-3353 defect family.

Drop the RequiresOutbox() gate. GH-3357's hasDatabaseBackedMessagePersistence
guard makes this safe: enlisting an endpoint that never cascades is a no-op
flush at commit time, and persistence-less applications keep their send-now
composition (pinned by a dedicated guard-rail test).

Red-first verified: the new codegen-surface test fails without the gate change.
Full Wolverine.Http.Tests 817/0, EfCoreTests 186/187 (known Bug_1846 load
flake), full wolverine.slnx Release build 0 warnings / 0 errors.

Closes #3358

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeremydmiller jeremydmiller merged commit 0dd45d2 into main Jul 10, 2026
25 checks passed
This was referenced Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EF Core Lightweight mode: HTTP endpoint that injects the DbContext and cascades only via tuple return is still sent before commit (RequiresOutbox gate)

1 participant