Repository navigation
JasperFx 2.48.0
What's Changed
Six projection-dispatch defects in the aggregate source generator and its runtime, plus a silent event-identity bug on string-keyed appends.
⚠️ Breaking: JFXEVT003 is now an Error
JFXEVT003 was Info — invisible in a CLI build at any verbosity — and its message promised a "fallback to runtime expression compilation" that does not exist. It is now an Error with an accurate message, and it fires in far fewer places.
A projection that needs partial and lacks it now fails the build instead of failing later at store construction with InvalidProjectionException: No source-generated dispatcher found. Every affected consumer is already broken at runtime today; this moves the failure earlier and names the fix. Suppressible with dotnet_diagnostic.JFXEVT003.severity if you need a staged upgrade.
partial is required in far fewer places
Projection subclasses were silently skipped unless declared partial — build clean, then a hard failure at DocumentStore construction. The gate predated #462, which moved aggregation dispatch to a standalone file-scoped evolver that needs nothing from the user's declaration. Rider's redundant-partial inspection would also strip the modifier back off, correctly by the language's rules, reintroducing the failure on the next cleanup-on-save.
After this release partial is required in exactly two places, both reported at build time:
- an
EventProjectionsubclass using conventional methods — its dispatcher is anApplyAsyncoverride on your class; - a generic projection, or one behind
private/protected, which afile-scoped type cannot name (and then every containing type must bepartialtoo).
Everything else — including DI-activated projections and every ordinary SingleStreamProjection / MultiStreamProjection subclass — needs no modifier. Generic and non-visible projections previously emitted uncompilable code (CS0246 / CS0122) even with partial; they now work. (#650, #651)
Other fixes
- The evolver now dispatches on the projection the store actually registered, instead of a
new'd orGetUninitializedObjectshadow — so injected dependencies are reachable and anything the real constructor set is present. This also removes the member-injection emission that had reintroduced the CS0111 double-load failure when the generator ships in two referenced packages (#653, #661) ShouldDeleteandCreate/Applycovering the same event type produced two identical switch arms and failed the consumer build with CS8120. They now compose into one arm — delete when the predicate says so, otherwise fold the event in. A ctor-registeredDeleteEvent<T>()stays unconditional (#652, #660)- A projection method that hides rather than overrides
Evolveis no longer treated as an override. Previously this either threw a bogus "can only use the override of 'Evolve' or conventional Apply/Create/ShouldDelete methods, but not both" at registration, or reached first-event dispatch and threwNotImplementedException. Resolution also no longer throwsAmbiguousMatchExceptionwhen the projection declares a same-named helper (#656, #659) - A non-partial
EventProjectionwith an explicitApplyAsyncoverride silently dropped its published-type registration with no diagnostic at all. Now reported as JFXEVT006 (Warning) naming the projection and the unregistered document types (#654, #658) JFXEVT001andJFXEVT004are removed. Neither was ever reported, and the lambda-registration opt-outJFXEVT004described could only produce false skips — theProjectEvent/CreateEvent/DeleteEventlambda APIs it matched were removed in JasperFx 2.0 / Marten 9.0, so it could only fire on a user-defined helper that happened to share a name (#655, #657)StreamAction.Append(graph, string streamKey, …)andStreamAction.Append(string streamKey, IEvent[])appended straight to the backing list instead of going throughAddEvents, so the envelopes never gotStreamId/StreamKey/TenantId.PrepareEventsdoes not close the gap either. A store handingstream.Eventsto an inline projection gave it envelopes with an emptyStreamKey— the normal way a string-identified projection learns which entity it is projecting — and the projection silently wrote a document with a blank field (#663, #664)
Note on version coupling
The generator and the JasperFx.Events runtime now move together: an evolver that takes the projection through its constructor cannot be activated by a runtime without activateEvolver. Pin JasperFx.Events and JasperFx.Events.SourceGenerator to the same version. Evolvers generated before this release keep working — the parameterless constructor is still the fallback.
Validation
Packed locally and validated against both stores before publishing:
- Marten master @
9ddfd0ac8—EventSourcingTests1850 passed / 0 failed,CoreTests535 passed / 0 failed, 0 build errors and noJFXEVTdiagnostics anywhere. - Polecat main @
70b4367—Polecat.Tests2151 passed / 0 failed, 0 build errors and noJFXEVTdiagnostics.
The JFXEVT003 escalation produces no diagnostics in either corpus, so the severity change should be inert for existing projections in both stores.
Full Changelog: V2.47.0...V2.48.0