GH-3956: recognise the JasperFx.Events.Documents contracts in all three stores - #3962
Merged
Merged
Conversation
…ee stores A handler could not take JasperFx.Events.Documents.IDocumentSessionOperations, IDocumentWriteOperations or IDocumentReadOperations as a parameter, which is the whole point of those contracts existing: store-agnostic source has no other way to take a session without naming a concrete store type. Two independent gaps, both of which had to close: 1. Codegen matches a variable by its EXACT type, so nothing satisfied the parameter from the IDocumentSession the chain had already created. Added a SharedDocumentOperationsSource per store, alongside the existing SharedEventOperationsSource -- the document-side counterparts to the event ones. 2. PersistenceFrameProvider.CanApply matched against a fixed type list that named none of them, so AutoApplyTransactions skipped the chain and no SaveChangesAsync postprocessor was attached. Measured, not assumed: on a stock host today the handler fails codegen outright with UnResolvableVariableException. Add only the variable source and it binds, runs, and the write is queued into the session's unit of work and SILENTLY DISCARDED -- the failure mode the issue reports, and confirmed here by reverting just the predicate and watching two of the new tests go red while the rest stay green. All three contracts resolve from the chain's single IDocumentSession, including the read-only one. Resolving reads from a separate IQuerySession would give a handler that takes read and write contracts together two different sessions, and its reads would not see its own pending writes. IDocumentReadOperations is probed but deliberately NOT matched in the predicate, exactly as IQuerySession has always been: a read-only parameter is not evidence that the chain writes anything. Also registers the contracts in DI -- the stores register only their own interfaces, so a service depending on one could not be resolved at all -- delegating to IDocumentSession/IQuerySession so Marten's GH-3001 scope priming still applies and a service-located contract gets the handler's outbox-enrolled session. IDocumentStore already implements IDocumentSessionFactory, so that is a straight singleton alias, the same move #430 made for IProjectionCoordinator. No package pin bump needed: JasperFx.Events 2.47.0 already carries every type, and Marten 9.23.0 / Polecat 5.12.0 / Fisher 0.7.0 all implement them. Verified by reflecting on the pinned assemblies rather than assuming. Note for adopters: importing the JasperFx.Events.Documents NAMESPACE makes ToListAsync() ambiguous between DocumentQueryableExtensions and each store's own queryable extensions (CS0121). The tests alias the individual contracts instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JG8Un6iNeyXECKJk3jo5uC
Member
Author
|
Full-suite regression results after the change:
Plus the new tests: |
This was referenced Aug 16, 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 #3956.
A handler could not take
JasperFx.Events.Documents.IDocumentSessionOperations,IDocumentWriteOperationsorIDocumentReadOperationsas a parameter — which is the whole point of those contracts existing, since store-agnostic source has no other way to take a session without naming a concrete store type.Two independent gaps, both of which had to close
IDocumentSessionthe chain had already created. Added aSharedDocumentOperationsSourceper store, alongside the existingSharedEventOperationsSource— the document-side counterparts to the event ones.CanApply. The fixed probe list/predicate named none of them, soAutoApplyTransactionsskipped the chain and noSaveChangesAsyncpostprocessor was attached.Measured, not assumed
The issue describes a silent write loss. On a stock host it's actually harder than that — the handler fails codegen outright with
UnResolvableVariableException, because the stores don't register the abstractions in DI either.The silent-loss half is real and was confirmed directly: with the variable source in place but the predicate reverted, the handler binds, runs, and the write is queued into the session's unit of work and discarded with no exception — two of the new tests go red while the other three stay green. Both halves are load-bearing.
Design notes
IDocumentSession, including the read-only one. Resolving reads from a separateIQuerySessionwould hand a handler that takes read and write contracts together two different sessions, and its reads would not see its own pending writes. There's a test pinning that they're the same instance.IDocumentReadOperationsis probed but deliberately NOT matched in the predicate, exactly asIQuerySessionhas always been: a read-only parameter is not evidence that the chain writes anything.IDocumentSession/IQuerySessionrather than opening a fresh session, so Marten's Codegen: prime scoped IServiceProvider via IRequireScopingFrame so MessageContext and IDocumentSession aren't duplicated under service location #3001 scope priming still applies and a service-located contract gets the handler's outbox-enrolled session.IDocumentStorealready implementsIDocumentSessionFactory, so that one is a straight singleton alias — the same move Requests with Accept: */* is dropped with 406 #430 made forIProjectionCoordinator.No package bump needed
The handoff notes assumed this would need
JasperFx.Events2.48.0. It doesn't — 2.47.0 already carries every type, and Marten 9.23.0 / Polecat 5.12.0 / Fisher 0.7.0 all implement them. Verified by reflecting on the pinned assemblies.One thing worth flagging for adopters
Importing the
JasperFx.Events.Documentsnamespace makesToListAsync()ambiguous betweenDocumentQueryableExtensionsand each store's own queryable extensions (CS0121). The tests alias the individual contracts instead of importing the namespace. That's upstream in JasperFx.Events, not something this PR can fix, but it will bite anyone converting a file to the store-agnostic contracts.Verification
store_agnostic_document_session_parameters(Marten): 5/5, confirmed red firstall_queryable_and_event_store_operationsclass (that suite balances CI shards by test-class count): 7/7 eachFisherTests: 25/25🤖 Generated with Claude Code
https://claude.ai/code/session_01JG8Un6iNeyXECKJk3jo5uC