Repository navigation
JasperFx 2.49.0
A by-id load for strong-typed-identity documents (#665, #666)
JasperFx.Events.Documents.IDocumentReadOperations gains one member:
Task<T?> LoadAsync<T>(object id, CancellationToken token = default) where T : notnull;Before this, the read contract offered Guid and string only, so a document keyed by a strong-typed identifier could not be loaded by id through the abstraction at all. Passing the wrapped primitive was not an alternative — it compiles and then throws the store's id-type mismatch at runtime — so store-agnostic code had to fall back to a LINQ query on the identity standing in for a load.
The typed overloads stay preferred by overload resolution, so no existing call site moves; only an argument that fits neither lands on the new member.
Not a compile break for stores
The member ships with a default implementation. It unboxes a Guid or string and forwards to the overload that already exists — the only half of the member that is answerable identically for every store — and throws NotSupportedException, naming the implementing type, the document type and the identity type, for anything else. Resolving a strong-typed identifier needs the store's own value-type registration, which nothing generic can guess.
So a store takes this release cleanly and overrides the member when it is ready. What holds it to the real behavior is the compliance suite rather than the compiler.
Compliance
DocumentLoadAndStoreCompliance gains a document keyed by a strong-typed id and three facts: the load, its miss case, and the one an override is most likely to get wrong — a boxed Guid or string reaching the object overload through an object-typed local must resolve exactly as the typed overload does. A store inheriting the default passes that third fact and fails the first two.
DocumentComplianceConfig gains ValueTypes / RegisterValueType<T>(), because those facts need the identity type registered with the store and the document contract does not carry identity configuration. Enrolling fixtures must replay it — every store spells it options.RegisterValueType(type). This is not a compile break either: a fixture builds fine without it and then fails three facts at runtime.
Store status
- Marten already declares and implements
LoadAsync<T>(object)— no product change. Fixture work only: JasperFx/marten#5241 - Polecat — JasperFx/polecat#472
- Fisher has
LoadAsync<T, TId>(TId), which does not satisfy the member on arity: JasperFx/fisher#89