Skip to content

F# codegen: Marten document slice — runnable sample + compile-gate (GH-2969)#2984

Merged
jeremydmiller merged 1 commit into
mainfrom
feat-2969-fsharp-marten-document
May 29, 2026
Merged

F# codegen: Marten document slice — runnable sample + compile-gate (GH-2969)#2984
jeremydmiller merged 1 commit into
mainfrom
feat-2969-fsharp-marten-document

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Part of the F# code-generation audit (#2969), following the EF Core slice (#2983). This is the second store-specific slice: Marten document persistence, with its own runnable F# sample + compile-gate.

Marten document frame F# emit

Both in Wolverine.Marten/Codegen — and no JasperFx release needed this time (both are Wolverine-side):

  • OpenMartenSessionFrameuse session = factory.OpenSession(context). C# await using var becomes F# use — inside the task { } CE that disposes the IAsyncDisposable session at block end. Covers the no-context (native Marten) and tenant-id branches too.
  • CreateDocumentSessionFrame → the create-session + eager-idempotency branches (use session = …, do! context.AssertEagerIdempotencyAsync(ct)).

SaveChangesAsync and the outbox flush are MethodCall/already-F# frames.

Generated F# (from the gate):

task {
    // Building the Marten session
    use documentSession = _outboxedSessionFactory.OpenSession(context)
    let createProductCommand = context.Envelope.Message :?> WolverineMartenFSharpSample.CreateProductCommand
    let outgoing1 = WolverineMartenFSharpSample.CreateProductHandler.Handle(createProductCommand, documentSession)
    do! context.EnqueueCascadingAsync(outgoing1)
    do! documentSession.SaveChangesAsync(cancellation)
    do! context.FlushOutgoingMessagesAsync()
}

Runnable sample — src/Samples/WolverineMartenFSharpSample

F# Product/CreateProductCommand/ProductCreated + a [<Transactional>] CreateProductHandler storing through an injected IDocumentSession. Marten is both the document store and (via IntegrateWithWolverine) the durable message store backing the transactional outbox. Postgres-backed — not infra-free (the static F# story is the compile-gate's job). Runs via dynamic codegen (Wolverine.RuntimeCompilation + opts.UseRuntimeCompilation(), #2876). Verified end-to-end: prints Created a Product through the F# Wolverine + Marten handler.

Compile-gate — src/Testing/Wolverine.Marten.FSharp{Tests,Fixture}

Renders the sample's real CreateProductCommand chain to F# via the no-host HandlerGraphAssembleTypesGenerateFSharpCode path, then dotnet builds the checked-in fixture (FS0193/lock retry). Mirrors the Core/Http/EfCore gates.

Wire-up

  • Sample + gate added to wolverine_fsharp.slnx.
  • fsharp.yml runs the Marten document gate as its own sequential step.

Verification

  • Marten document compile-gate green.
  • dotnet build wolverine.slnx -c Release clean (per CLAUDE.md the slim build isn't sufficient).
  • Sample runs end-to-end on Postgres.

Next slice: the Marten event-sourced aggregate (MartenOps.StartStream, [Aggregate] loading) — the heaviest frame set.

🤖 Generated with Claude Code

…-2969)

Second store-specific slice of the F# code-generation audit: a runnable F#
Wolverine + Marten document app and a compile-gate proving its handler chain
emits valid F# through Wolverine's static codegen path.

Marten document frame F# emit (both in Wolverine.Marten/Codegen):
- OpenMartenSessionFrame      -> `use session = factory.OpenSession(context)`
  (`await using var` becomes F# `use`; the task { } CE disposes the
  IAsyncDisposable session at block end). Covers the no-context / tenant-id
  branches too.
- CreateDocumentSessionFrame  -> the create-session + eager-idempotency branches
  (`use session = …`, `do! context.AssertEagerIdempotencyAsync(ct)`).
SaveChangesAsync + the outbox flush are MethodCall/already-F# frames.

Runnable sample (src/Samples/WolverineMartenFSharpSample):
- F# Product/CreateProductCommand/ProductCreated + a [<Transactional>]
  CreateProductHandler storing through an injected IDocumentSession.
- Marten as both the document store and (via IntegrateWithWolverine) the durable
  message store; Postgres-backed (the *static* F# story is the compile-gate's job).
  Runs via dynamic codegen (Wolverine.RuntimeCompilation + UseRuntimeCompilation,
  GH-2876). Verified end-to-end: "Created a Product through the F# Wolverine + Marten handler."

Compile-gate (src/Testing/Wolverine.Marten.FSharp{Tests,Fixture}):
- Renders the sample's real CreateProductCommand chain to F# via the no-host
  HandlerGraph/AssembleTypes/GenerateFSharpCode path and dotnet-builds the fixture.

Wire-up: sample + gate added to wolverine_fsharp.slnx; fsharp.yml runs the Marten
gate as its own sequential step. No JasperFx release needed (both frames are
Wolverine-side).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller jeremydmiller merged commit ea91895 into main May 29, 2026
23 of 24 checks passed
This was referenced Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant