Follow-up to #2919: docs + extended scenario tests for DbContext abstractions#2954
Merged
Conversation
…ractions Reviewer-requested follow-up to PR #2919 (DbContext abstractions support via ). Adds three new scenarios in src/Persistence/EfCoreTests/dbContext_abstraction_scenarios.cs: 1. multi-DbContext, mixed abstraction - one DbContext registered through an abstraction (IOrderRepository -> OrdersDbContext) and a second DbContext used directly (CustomersDbContext). One Wolverine host; both handlers transact through the EF Core middleware in parallel. 2. multiple abstractions for the SAME DbContext - StoreDbContext implements IItemRepository AND IOrderInsightRepository. Two separate handlers, each depending on a different abstraction, both successfully commit through the same physical DbContext. 3. multiple abstractions used IN THE SAME handler - the test handler takes BOTH IItemRepository and IOrderInsightRepository. The assertion proves that at runtime both parameters resolve to the *same scoped* StoreDbContext via ReferenceEquals on the casts. That's the contract the cast frame the merged PR emits relies on: one DbContext in scope, viewed through different interfaces. The forwarding-factory DI shape (services.AddScoped<TAbs>(sp => sp.GetRequiredService<TDb>())) is what makes this work; AddScoped<TAbs, TImpl>() would create separate DbContext instances per registered interface and the cast would land on a different one than the handler's reference. Schemas are dropped + recreated with raw DDL before each scenario starts (mirroring the workaround documented in Bug_DurableLocalQueue_ancillary_store_routing for GH-2618 - EF Core's EnsureCreatedAsync is a no-op when the database already exists, so the user-defined tables never get materialised on the shared Wolverine integration-tests Postgres unless we issue DDL ourselves). The new file lives in a sub-namespace so its IOrderRepository / OrderEntity / etc. fixtures don't collide with the unrelated identically-named fixtures in Bug_252_codegen_issue.cs under the parent namespace. Also adds documentation: - new section in docs/guide/durability/efcore/transactional-middleware.md - Five blocks in the new test file (the registration shape, the simple-handler shape, the multi-abstraction registration, the multi-abstraction handler, and the mixed-DbContext registration), expanded by mdsnippets so every code block in the docs lives in a passing test. Local: full wolverine.slnx -c Release builds clean (0 warnings, 0 errors). All three scenarios pass (Postgres locally on port 5433). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jeremydmiller
added a commit
that referenced
this pull request
May 28, 2026
Bug-fix + feature release on top of 6.1.0 — 13 PRs. Notable additions: - Custom Result<T> handler-return-value support (Phases 0+1+2+3, #2952, refs #2221) - DbContext abstractions for EF Core transaction middleware (#2919 + docs/tests #2954) - Outgoing Envelope pooling at MessageRouter.RouteForPublish (#2956, closes #2955) — ~-504 B/op on transport-bound sends per the CritterStackScalability WolverineTransportBenchmarks harness Bug fixes: scheduled-cascade loss from [ReadAggregate]/[DocumentExists] handlers (#2941), ancillary-store inbox routing (#2944), Postgres queue-name length (#2942), MySQL node-record quoting (#2940), Pulsar batched-partition ack KeyNotFoundException (#2883/#2950), remote-node agent reply timeout (#2949), and additional resource-disposal cleanup (#2894 from dmytro-pryvedeniuk). Polecat bumped 4.1.1 -> 4.2.1 (#2947); Marten + JasperFx families unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Reviewer-requested follow-up to #2919 (DbContext abstractions support via
WithDbContextAbstraction<TAbstraction, TDbContext>()).Three new test scenarios
A new file at
src/Persistence/EfCoreTests/dbContext_abstraction_scenarios.cscovers contracts that the merged PR's tests don't reach:multiple_dbcontext_types_one_abstracted_one_direct— one Wolverine host with twoDbContexts:OrdersDbContextregistered through anIOrderRepositoryabstraction andCustomersDbContextused directly. Both handlers transact correctly through the EF Core middleware in parallel.multiple_abstractions_for_same_dbcontext_each_used_independently—StoreDbContextimplements bothIItemRepositoryandIOrderInsightRepository. Two separate handlers, each depending on a different abstraction, both successfully commit through the same physicalDbContext.handler_using_two_abstractions_to_same_dbcontext_uses_one_scoped_instance— the headline scenario. The handler takes BOTHIItemRepositoryandIOrderInsightRepository, casts both back toStoreDbContext, and assertsReferenceEquals(itemsCtx, ordersCtx). That's the contract the mergedCastDbContextFramedepends on: at runtime, both parameters resolve to the same scopedDbContext, just viewed through different interfaces, so a singleSaveChangesAsynccommits all writes atomically.A note about #3: the contract only holds when the abstractions forward to the same scoped
DbContextin DI via a factory:services.AddScoped<IItemRepository, StoreDbContext>()(without the factory) would create a separateDbContextinstance per registered interface, and the cast frame would land on whichever the codegen picked first while the handler's other reference points at a different one. The new docs section calls this out explicitly.Docs
New
DbContext Abstractionssection at the bottom ofdocs/guide/durability/efcore/transactional-middleware.md. Five#region sample_*blocks in the test file, expanded bymdsnippetsso every code block in the doc lives in a passing test:sample_register_dbcontext_abstraction— the bare single-abstraction registration shapesample_handler_using_dbcontext_abstraction— handler depending on the abstractionsample_register_multiple_dbcontext_abstractions— two abstractions, factory-forwarded to the same scoped DbContextsample_handler_using_multiple_abstractions— handler taking both abstractions; ref-equality contractsample_register_mixed_dbcontexts— multi-DbContext, mixed abstraction registrationThe rest of the doc diffs in this PR are mdsnippets regenerations of unrelated snippets it touched while running over the whole repo.
Schema setup detail (mirrors the GH-2618 workaround)
EF Core's
EnsureCreatedAsyncis a no-op when the database already exists — and the shared Wolverine integration-tests Postgres always does. So each scenario drops + creates its test schemas and then issues rawCREATE TABLEDDL for the user-defined entity tables, the same patternBug_DurableLocalQueue_ancillary_store_routinguses for the same reason.Verification
dotnet build wolverine.slnx -c Releaseclean (0 warnings, 0 errors).mdsnippetsexpansion of the new doc anchors succeeded and is committed.🤖 Generated with Claude Code