Fix DI-activated projection evolver + multi-identity dispatcher (2.14.1)#470
Merged
Conversation
…lver + multi-identity dispatcher Two regressions surfaced when Marten/Polecat adopt the 2.14.x event-sourcing codegen (Marten master deliberately pinned the older generator to avoid them): 1. Generator (#4185): a projection registered via AddProjectionWithServices has only a constructor with injected dependencies (no parameterless ctor). The AggregateEvolver generator created the evolver's private shadow projection instance with `new T()`, which fails to compile (CS7036). When there is no public parameterless ctor, instantiate the shadow instance without running the constructor via RuntimeHelpers.GetUninitializedObject (mirrors the aggregate no-parameterless-ctor fallback). The shadow instance only invokes the projection's stateless Create/Apply event methods. 2. Runtime (#297 multi-identity): when one aggregate is registered against multiple identity types (e.g. AggregateStream<CountOfLetters> with both Guid and string ids), the generator emits one self-aggregating evolver per TId, all keyed on the aggregate type with a null ProjectionType. tryUseAssemblyRegisteredEvolver selected the fallback by aggregate type alone and could pick the wrong-TId evolver, whose strongly-typed IGenerated*<TDoc,TId> interface checks then all failed, leaving _evolve unwired and tripping the 'no source-generated dispatcher' backstop. The fallback now only accepts an evolver that implements a <TDoc, TId> contract for the current identity type. Adds a generator regression test; Marten's live_aggregation_with_string_identifiers + Bug_4185 cover the runtime + generator paths (full EventSourcingTests green). Version → 2.14.1. Co-Authored-By: Claude Opus 4.8 (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.
Two event-sourcing codegen regressions that surface when Marten/Polecat adopt the 2.14.x line (Marten master deliberately pinned the older generator to avoid them). Found while publishing the document-diagnostics stack (#469).
1. Generator — DI-activated projection (CS7036)
A projection registered via
AddProjectionWithServiceshas only a constructor with injected dependencies (no parameterless ctor). TheAggregateEvolvergenerator created the evolver's private shadow projection instance withnew T(), which fails to compile (CS7036) — e.g. Marten's #4185OrderProjection4185 : SingleStreamProjectionwith anIDocumentStorector param. When there is no public parameterless ctor, the shadow instance is now created without running the constructor (RuntimeHelpers.GetUninitializedObject), mirroring the aggregate no-parameterless-ctor fallback. The shadow instance only invokes the projection's stateless Create/Apply event methods.2. Runtime — multi-identity dispatcher (#297)
When one aggregate is registered against multiple identity types (e.g.
AggregateStream<CountOfLetters>with both Guid and string ids), the generator emits one self-aggregating evolver per TId, all keyed on the aggregate type with a nullProjectionType.tryUseAssemblyRegisteredEvolverselected the fallback by aggregate type alone and could pick the wrong-TId evolver, whose strongly-typedIGenerated*<TDoc,TId>interface checks then all failed — leaving_evolveunwired and tripping the "no source-generated dispatcher" backstop. The fallback now only accepts an evolver implementing a<TDoc, TId>contract for the current identity type.Validation
di_activated_projection_without_parameterless_ctor_compiles); generator suite 24/24.Bug_4185(generator) +live_aggregation_with_string_identifiers(runtime) both pass; full Marten EventSourcingTests 1378/0/7 green against 2.14.1.🤖 Generated with Claude Code