Skip to content

Update NaturalKeyProjection for the widened JasperFx.Events natural key extraction contract (jasperfx#569) #369

Description

@jeremydmiller

Follow-up to JasperFx/jasperfx#569 / PR JasperFx/jasperfx#571. Sibling of JasperFx/marten#5052 — jasperfx#569 was written believing Marten was the only consumer of this API, but Polecat maintains its own pc_natural_key_X lookup table off the same NaturalKeyDefinition.

Required change (one line)

NaturalKeyEventMapping.Extractor changes from Func<object /* event data */, object?> to Func<IEvent, object?>. Widening it to the whole event is what makes an IEvent<T> [NaturalKeySource] handler bindable at all, and what lets a key be derived from event metadata.

Polecat has a single call site, and it already holds the real IEvent:

  • src/Polecat/Events/Projections/NaturalKeyProjection.cs:96mapping.Extractor(e.Data)mapping.Extractor(e)

QueueOperationForEvent is shared by both the inline append path (ApplyAsync) and the rebuild path (QueueUpsertsForEvents), so that covers both.

What Polecat inherits for free

The discovery bugs are all upstream, so Polecat has the same exposure today and gets the same fixes on the version bump:

  • IEvent<T> handlers were silently dropped. A [NaturalKeySource] method taking IEvent<T> produced no mapping, no error and no log, so pc_natural_key_X was simply never written for that event type — a stale lookup after a key change, live and on rebuild alike. Worth a regression test on the Polecat side mirroring marten#5042's bug_5041_natural_key_should_be_updated_during_rebuild_when_handler_is_ievent, since Polecat's rebuild path (DocumentStore.EventStore.cs Natural-key lookup table not repopulated on projection rebuild (parity with marten#4788) #259 re-emit) is its own code.
  • Handlers were invoked against a fabricated blank aggregate. Expression.New(docType) bypasses required-member enforcement, so a handler body touching any state other than the key threw out of the extractor and aborted the caller's SaveChangesAsync. That path is now last-resort and gated on the aggregate being safely constructible.
  • Unbindable [NaturalKeySource] methods now throw InvalidProjectionException from AssembleAndAssertValidity() instead of registering nothing. Worth a pass over the Polecat test suite for any projection quietly relying on a natural key mapping that never existed.

Also worth fixing while here

src/Polecat/Events/EventOperations.cs:748 tells users to "Configure a natural key via NaturalKey() in a SingleStreamProjection registration" — there is no such API, in Polecat or in JasperFx.Events. Until jasperfx#571 the only candidate (NaturalKeyBuilder<TDoc>.SetBy) had an internal constructor and was unreachable dead code, so that message pointed users at nothing.

jasperfx#571 makes it real as NaturalKeyFor():

NaturalKeyFor(x => x
    .SetBy<ProductRegistered>(e => new ProductCode(e.Code))
    .SetByEvent<ProductCodeChanged>(e => new ProductCode(e.Data.NewCode)));

So the message should name NaturalKeyFor(), and docs/events/natural-keys.md should document it as the explicit alternative to [NaturalKeySource] — plus the constraint the attribute path now enforces: the key has to be a function of the event alone, since the lookup table is maintained inline at append time where no prior aggregate exists under an Async snapshot lifecycle.

Blocked on a JasperFx.Events release carrying jasperfx#571.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions