fix(generator): register published types via a PublishedTypes() override (marten#5192) - #637
Merged
Merged
Conversation
…ide (marten#5192)
An EventProjection's discovered published document types were registered by emitting
`public MyProjection() { RegisterPublishedType(...); }` into the user's partial class.
A constructor is the wrong extension point, and it failed two ways.
It is illegal on a type that declares a primary constructor --
`partial class MyProjection(ILogger logger) : EventProjection` -- because C# requires
every other constructor to chain through the primary one, so the generated file broke
the build outright with CS8862. That is what marten#5192 reported.
Worse, and silent: a projection that takes dependencies has to be registered through
Marten's AddProjectionWithServices, so the container calls the dependency-taking
constructor and the generated parameterless one never ran. Published types went
unregistered, which also left #626's teardown registration -- it reads
PublishedTypes() -- with nothing to register.
Emit an override of the virtual ProjectionBase.PublishedTypes() instead. It does not
care how the instance was constructed, and chaining through base.PublishedTypes()
keeps hand-written RegisterPublishedType calls and Options.StorageTypes flowing. The
generator yields when the author already wrote their own override.
Both emission sites are covered: EmitEventProjectionTypeRegistrationPartial (an
ApplyAsync override) and the conventional-method path.
Neither defect was reachable before 2.38.0: discovery was syntactic, so only an
explicit `Store<Doc>(x)` produced a registration and the far more common `Store(doc)`
produced none. #611 made discovery semantic and both spellings started
emitting the constructor.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde
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.
Fixes the source generator break reported in marten#5192.
The bug
An
EventProjection's discovered published document types were registered by emitting a parameterless constructor into the user's partial class:A constructor is the wrong extension point, and it failed two ways.
1. It broke the build (the reported symptom). On a type that declares a primary constructor —
partial class MyProjection(ILogger<MyProjection> logger) : EventProjection— C# requires every other constructor to chain through the primary one, so the generated file failed the whole compilation with CS8862.2. It silently did nothing, which is worse. A projection that takes dependencies has to be registered through Marten's
AddProjectionWithServices, so the container calls the dependency-taking constructor and the generated parameterless one never ran. Published types went unregistered, and#626's teardown registration — which readsPublishedTypes()— had nothing to register. Verified against Marten 9.22.4:The fix
Emit an override of the virtual
ProjectionBase.PublishedTypes()instead:An override does not care how the instance was constructed, and chaining through
base.PublishedTypes()keeps hand-writtenRegisterPublishedTypecalls andOptions.StorageTypesflowing. The generator yields when the author already wrote their own override. Both emission sites are covered:EmitEventProjectionTypeRegistrationPartial(anApplyAsyncoverride) and the conventional-method path.The emitted code deliberately avoids LINQ —
CompileWithGeneratorin the test harness does not referenceSystem.Linq, and generated code should not assume a consumer's reference set either.Regression window
Neither defect was reachable before 2.38.0. Discovery used to be syntactic, so only an explicit
Store<Doc>(x)produced a registration and the far more commonStore(doc)produced none at all. #611 made discovery semantic and both spellings started emitting the constructor. Bisected on the consumer side: Marten 9.22.2 builds, Marten 9.22.3 does not.Behavior change worth a second look
The old guard skipped registration entirely when the class already had an explicit parameterless constructor — it existed only because you cannot add a second one. An override has no such conflict, so those projections now get their published types registered too. That is the intended #4166 behavior, but it means an upgrade can newly provision document storage, and newly register teardown targets under #626, for a projection that was quietly getting neither.
Verification
EventTests730/730 on net9.0 and net10.0; full solution builds.PublishedTypes: [Thing]. FullMarten.slnxbuilds and Marten'sEventSourcingTestsruns 1627 passed / 0 failed / 7 skipped.🤖 Generated with Claude Code
https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde