fix(#4966): natural key extractor handles a two-arg static evolve method#528
Merged
Merged
Conversation
A natural key that CHANGES via an update event goes unrecorded: the aggregate's `[NaturalKeySource]` evolve method — `static TDoc Apply(TEvent e, TDoc current)` — was silently skipped during natural-key discovery, so no NaturalKeyEventMapping was built for the update event. The `mt_natural_key_X` lookup table therefore only ever recorded the CREATE event's key and never the changed key (on live append OR rebuild), leaving FetchLatest-by-natural-key unable to find the stream under its new key. Reported against a projection rebuild in marten#4966. Root cause: buildExtractor's static-factory branch matched any static method on the aggregate returning the aggregate type but built a one-argument call, so a two-arg evolve method threw ArgumentException while composing the Expression.Call and was swallowed by the surrounding catch. Now the branch builds one argument per parameter: the event parameter receives the raw event data (converted); a TDoc parameter (the prior aggregate) receives a fresh default aggregate, mirroring the instance-method branch. Parameters that can't be satisfied from event data alone (IEvent<T> metadata, or a doc type with no public parameterless ctor) fall through to the existing property-matching path, so no previously working shape regresses. Adds NaturalKeySourceDiscoveryTests coverage for the evolve shape and bumps to 2.28.1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XKKBPsFJ83jd2o4hyGJnj6
This was referenced Jul 17, 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
A natural key that changes via an update event goes unrecorded. Given a self-aggregating aggregate whose
[NaturalKeySource]methods are:the two-arg
Applyevolve method was silently skipped during natural-key discovery, so noNaturalKeyEventMappingwas built forProductCodeChanged. Themt_natural_key_Xlookup table only ever recorded the create event's key and never the changed key — on live inline append or rebuild — leavingFetchLatest-by-natural-key unable to find the stream under its new key.Reported against a projection rebuild in marten#4966.
Root cause
buildExtractor's static-factory branch matched any static method on the aggregate returning the aggregate type, but composed a one-argumentExpression.Call. A two-arg evolve method threwArgumentExceptionwhile building the call, which the surroundingcatch { /* silently skip */ }swallowed.Fix
The static-on-aggregate branch now builds one argument per parameter:
TDocparameter (the prior aggregate) receives a fresh default aggregate — mirroring the existing instance-method branch.Parameters that can't be satisfied from event data alone (
IEvent<T>metadata, or a doc type with no public parameterless ctor) fall through to the existing property-matching path, so no previously working shape regresses. All 91EventTestsprojection tests pass.Verification
NaturalKeySourceDiscoveryTests.discovers_natural_key_source_on_static_evolve_method_that_changes_the_key.Ships as 2.28.1. The superseding Marten PR (retaining the original repro author) bumps to it and closes marten#4966.
🤖 Generated with Claude Code