Explicit transactional DbContext selection for multi-DbContext handlers (supersedes #3284)#3295
Merged
jeremydmiller merged 1 commit intoJul 5, 2026
Conversation
…Context handlers
A handler/HTTP/gRPC chain can legitimately depend on more than one DbContext — one
written through, one read-only. Previously EFCorePersistenceFrameProvider threw the
moment it saw a second DbContext-shaped dependency with no way to disambiguate.
Now the transactional DbContext can be designated explicitly — no "magic", no
automatic/registration-side inference:
* [Transactional(typeof(MyDbContext))] — carried as a chain tag; the attribute lives
in core Wolverine and only stores a System.Type, so it never references EF Core.
* [Storage(typeof(MyDbContext))] — the provider-agnostic storage attribute, now taught
to designate an EF Core DbContext via a new EFCoreDbContextStorageFrameProvider
(IAncillaryStoreFrameProvider). Read directly off the handler so it is honored even
under AutoApplyTransactions, whose policy runs before AncillaryStoreType is populated.
* Either may name a concrete DbContext or a registered DbContext abstraction
(WithDbContextAbstraction<TAbstraction, TDbContext>()).
When a chain has more than one DbContext dependency and no designation, Wolverine fails
fast at startup with a message telling the user to remove the automatic transactional
middleware or designate the context with [Transactional]/[Storage]. A designation that
names a non-dependency also fails loudly.
Supersedes #3284: keeps that PR's [Transactional]-attribute tests and drops its
registration-side auto-selection (WithTransactionalDbContext / Wolverine-enabled
inference) per the "explicit only" design. Adds [Storage] + ambiguity-validation tests
and docs.
Co-authored-by: KhaledZaabat <khaled.zaabat@ensia.edu.dz>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 9, 2026
Merged
This was referenced Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #3284 (by @KhaledZaabat, preserved as a co-author). Same core need — let a handler that depends on more than one
DbContextsay which one is transactional — but reworked to be explicit only, no magic, per review.Problem
A handler / HTTP endpoint / gRPC endpoint can legitimately depend on two
DbContext-shaped services: one it writes through (must be enrolled in the transaction + outbox) and one it only reads from.EFCorePersistenceFrameProviderused to throw the moment it saw a second one, with no way to disambiguate.Solution — explicit designation, no inference
Two equivalent, EF-Core-agnostic markers designate the transactional context:
[Transactional(typeof(MyDbContext))]— carried as a chain tag. The attribute lives in the coreWolverineassembly and only stores aSystem.Type, so neither it nor your handler assembly references EF Core.[Storage(typeof(MyDbContext))]— the provider-agnostic storage attribute (the same one used for Marten/Polecat ancillary stores), now taught to designate an EF CoreDbContextvia a newEFCoreDbContextStorageFrameProvider(IAncillaryStoreFrameProvider). It's read directly off the handler so it's honored even underAutoApplyTransactions, whose policy runs beforeAncillaryStoreTypeis populated.Either may name a concrete
DbContextor a registered abstraction (WithDbContextAbstraction<TAbstraction, TDbContext>()), so Clean Architecture handlers never touch the concrete EF type.When a chain has >1
DbContextand no designation, Wolverine fails fast at startup:A designation naming a type the chain doesn't actually depend on also fails loudly.
What changed vs #3284
[Transactional(typeof(X))]tag mechanism and its tests (transactional_dbcontext_selection_tests.cs).WithTransactionalDbContext<T>()selection and the automatic "single Wolverine-enabled candidate" inference — that's the "magic" the review asked to remove.[Storage(typeof(X))]support + the ambiguity-validation test (storage_dbcontext_selection_tests.cs).Verification (local, net9.0, Postgres + SQL Server)
transactional_dbcontext_selection_tests(3) +storage_dbcontext_selection_tests(3): 6/6.transaction_middleware_mode_tests: 8/8.EfCoreTests: 183/186; the 3 stragglers are pre-existing environmental flakiness (verified: one passes in isolation, one fails on cleanmain, one flakes) — none touch DbContext selection.Wolverine+Wolverine.EntityFrameworkCoreclean.Closing #3284 in favor of this. Full credit to @KhaledZaabat.
🤖 Generated with Claude Code