fix(efcore): run domain-event scrapers on managed multi-tenancy commit path#3060
Merged
jeremydmiller merged 1 commit intoJun 9, 2026
Conversation
…t path The EF Core transactional middleware has two codegen paths. The single-DbContext path commits through EfCoreEnvelopeTransaction.CommitAsync(), which runs the IDomainEventScraper instances registered by PublishDomainEventsFromEntityFrameworkCore<T>(x => x.Events). The managed multi-tenancy path (StartDatabaseTransactionForDbContext / CommitTenantedDbContextTransaction) committed the raw EF transaction directly and never invoked CommitAsync(), so the scrapers never ran and a mutated entity's domain events were silently dropped under managed multi-tenancy. The enlisted EfCoreEnvelopeTransaction is built at runtime inside IDbContextBuilder<T>.BuildAndEnrollAsync, so it is never surfaced as a codegen variable on this path and its CommitAsync() cannot be called from generated code. Inline the scrape loop into CommitTenantedDbContextTransaction instead, resolving IEnumerable<IDomainEventScraper> exactly as EnrollDbContextInTransaction does and running it before the commit + flush. JasperFxGH-2917 ordering and the IFlushesMessages contract are preserved. Adds a managed-multi-tenancy regression test mirroring the single-DbContext domain-event scraper tests.
2284746 to
9da4354
Compare
Member
|
@outofrange-consulting Why are you even using the domain event scraping? Was that legacy code you retrofitted to Wolverine? |
This was referenced Jun 10, 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.
Problem
Under managed multi-tenancy (
AddDbContextWithWolverineManagedMultiTenancy<T>), domain events registered viaPublishDomainEventsFromEntityFrameworkCore<T>(x => x.Events)are silently never published. A handler can mutate/persist an entity that raises a domain event, the transaction commits, but the event never reaches the outbox.Root cause
The EF Core transactional middleware has two codegen paths:
EfCoreEnvelopeTransaction.CommitAsync()(Internals/EfCoreEnvelopeTransaction.cs), which runs the registeredIDomainEventScrapers — emitted byCommitEfCoreEnvelopeTransaction(Codegen/EnrollDbContextInTransaction.cs).CommitTenantedDbContextTransaction.GenerateCode(Codegen/StartDatabaseTransactionForDbContext.cs) and never invokesCommitAsync(). The enlistedEfCoreEnvelopeTransactionis built (Internals/TenantedDbContextBuilderByConnectionString.csBuildAndEnrollAsync), but at runtime inside the DbContext builder — so it is never surfaced as a codegen variable and its scraper loop is unreachable from the generated chain.Fix
Inline the scrape loop into
CommitTenantedDbContextTransaction, resolvingIEnumerable<IDomainEventScraper>exactly asEnrollDbContextInTransaction.FindVariablesdoes and running it before the commit + flush — mirroringEfCoreEnvelopeTransaction.CommitAsync(). The GH-2917 ordering (commit + flush in the postprocessor before the HTTP response writer) and theIFlushesMessagescontract are preserved, so no duplicate flush is introduced.Surfacing the enlisted
EfCoreEnvelopeTransactionas a codegen variable (the cleaner alternative) is not feasible on this path: the DbContext and its transaction are constructed at runtime insideBuildAndEnrollAsync, never returned as a codegen variable, soFindVariable(typeof(EfCoreEnvelopeTransaction))would throw at generation time.Test
Adds
domain_events_scraping_with_managed_multi_tenancyunderEfCoreTests.MultiTenancy, the managed-multitenancy counterpart of the single-DbContext scraper tests inEfCoreTests/DomainEvents/configuration_of_domain_events_scrapers.cs. It approves an entity under a tenant and asserts the scraped domain event is published via a tracked session. The test fails onmainand passes with this change. Existing PostgreSQL multitenancy suites (cascading messages, HTTP posts, sagas, outbox) still pass; the empty-scraper case resolves to an empty enumerable, identical to the single-DbContext path.Affected versions
V6.0.0 – V6.5.1 and
main. The omission predates and is independent of #2920 / GH-2917, which only concern commit/flush ordering relative to the HTTP response writer.