Fix [Entity]/storage-action regression for non-EFCore-persisted sagas (#3342) - #3347
Merged
Merged
Conversation
…#3342) Since 6.5.0 (#3040), a saga whose Start/Handle returns an EF Core storage action (Insert<T>/Update<T>/...) of a NON-saga entity silently stopped persisting that write when the saga's OWN state is persisted by a different store (e.g. the SqlServer message store) rather than EF Core. A later handler that loads the same entity via [Entity] then got null: a [Entity(Required = false)] handler NRE'd on it, and a Required [Entity] handler was silently skipped. Root cause: #3040 fixed GH-3039 by making EFCorePersistenceFrameProvider's entity overload of ApplyTransactionSupport a no-op for ALL saga chains, deferring to the no-entity overload that SagaChain.DetermineFrames calls after every IHandlerPolicy (so a policy-set transaction mode is honored). But DetermineFrames calls the persistence provider that owns the SAGA. When that provider is NOT EF Core, EF Core's no-entity overload never runs, so the blanket early-return dropped EF Core's SaveChanges/transaction for the storage action entirely and the entity write was lost. Fix: only defer when EF Core actually persists the saga (TryDetermineDbContextType(saga.SagaType) != null). Otherwise fall through and apply EF Core's transaction support for the storage action as before. Preserves the GH-3039 fix (EFCore-persisted sagas still defer, honoring policy-set modes). Reproducing test (Bug_3342): a saga persisted by the SqlServer message store whose Start inserts an EF Core entity and cascades a command whose handler loads that entity via [Entity]. Fails on current main (NRE: the inserted record is null), passes with the fix. Kafka-free (local transports) and without the reporter's OnException.Discard so the real failure surfaces. Marked [WolverineIgnore] + IncludeType so the saga's unmapped storage action doesn't pollute sibling tests' conventional discovery. GH-3039 regression suite and the EFCore saga/storage/ dbcontext-selection suites stay green (80). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 9, 2026
Merged
This was referenced Jul 27, 2026
Closed
This was referenced Aug 3, 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.
Closes #3342.
Symptom (regression 6.4.3 → 6.5.0+)
A saga whose handler returns an EF Core storage action (
Insert<T>/Update<T>/…) of anon-saga entity, then cascades a message whose handler loads that same entity via
[Entity], broke at 6.5.0. The[Entity]load came back null:[Entity(Required = false)]handler threw aNullReferenceExceptionon it, andRequired[Entity]handler was silently skipped (so the cascade downstream never ran).The reporter's
OnException(...).Discard()masked the NRE, making it look like "the handler justdidn't execute."
Root cause
#3040 (GH-3039) made
EFCorePersistenceFrameProvider's entity overload ofApplyTransactionSupporta no-op for all saga chains:The intent: defer to the no-entity overload that
SagaChain.DetermineFramescalls after everyIHandlerPolicy, so a policy-set transaction mode is honored. ButDetermineFramescalls thepersistence provider that owns the saga's own state. When the saga is persisted by a different
store (here the SqlServer message store) and only its storage action uses EF Core, EF Core's
no-entity overload is never called — so the blanket early-return dropped EF Core's
SaveChanges/transaction for the storage action entirely, and the entity write was lost. A later[Entity]load of that entity then found nothing.This only affected sagas not persisted by EF Core; EF-Core-persisted sagas (the GH-3039 case)
were fine because their own provider IS EF Core.
Fix
Defer only when EF Core actually persists the saga:
Otherwise fall through and apply EF Core's transaction support for the storage action as before. The
GH-3039 fix is preserved (EF-Core-persisted sagas still defer, honoring policy-set modes).
Tests
Bug_3342_saga_entity_and_storage_action: a saga persisted by the SqlServermessage store whose
StartdoesStorage.Insert<OrderProcessRecord>and cascades a command whosehandler loads that record via
[Entity]. Fails onmain(NRE — the inserted record is null),passes with the fix. Kafka-free (transports are local) and without
OnException.Discard, so the realfailure surfaces. The saga is
[WolverineIgnore]+IncludeTypeso its unmapped storage action doesnot pollute sibling tests' conventional discovery.
transaction_middleware_mode_tests) and the EFCoresaga / storage-action / dbcontext-selection suites stay green (80 tests).
🤖 Generated with Claude Code