Skip to content
Orleans.FSharp logo

Orleans.FSharp

Build Orleans grains in idiomatic F#. User-authored API records, immutable state transitions, and zero C# stubs — with transactions, event sourcing, and streaming replies as first-class operations. 2,500+ tests.

Microsoft Orleans is one of the strongest virtual actor runtimes on .NET. But in raw C# style, F# teams often end up with mutable state classes, attribute noise, and repetitive infrastructure code.

Orleans.FSharp keeps full Orleans power while giving you a functional-first authoring model: a grain’s public surface is a plain F# record of functions, its behavior is a set of pure state -> reply handlers, and there is no interface, attribute, or code-generation bridge to write.

type ICounterGrain =
inherit IGrainWithStringKey
abstract Increment: unit -> Task<int>
abstract Value: unit -> Task<int>
// ...plus a C# shim project in the solution, because Orleans'
// proxy source generator does not run on F# projects.
type CounterGrain() =
inherit Grain()
let mutable count = 0
interface ICounterGrain with
member _.Increment() =
count <- count + 1
Task.FromResult count
member _.Value() = Task.FromResult count

Orleans.FSharp — the functional grain runtime

Section titled “Orleans.FSharp — the functional grain runtime”
type CounterApi =
{ increment: unit -> Task<int>
value: unit -> Task<int> }
let counterContract =
contract<string, CounterApi> {
grainType "counter"
version 1
stringKey
readOnly (_.value)
}
let counter =
grainFor counterContract {
defaultState (fun () -> 0)
handle (_.increment) (fun _ctx n () -> task { return n + 1, n + 1 })
handleQuery (_.value) (fun _ctx n () -> task { return n })
}

And the call site is the record itself — no proxy interface, no cast:

let api = FunctionalGrain.ref counterContract factory "my-counter"
let! n = api.increment ()

Same two operations, same reply types — but state transitions are explicit values, the compiler checks every handler against the record’s field types, and sealing the definition verifies that each operation has exactly one handler. No interface, no shim project, no proxy generation.

MetricValue
Test suite2,500+ unit + integration tests
Orleans support10.1.0 – 10.2.2, CI-tested at both ends of the range
Feature tour16 Orleans capabilities demonstrated live in examples/feature-tour
NuGet packages8 — core, runtime, abstractions, testing, event sourcing, codegen, analyzers, templates
LicenseMIT
Dispatch overheadunder 5% versus calling the handler function directly, measured over 1,000,000 iterations
Terminal window
dotnet new install Orleans.FSharp.Templates
dotnet new orleans-fsharp -n MyApp
cd MyApp
dotnet build && dotnet test

A working Orleans.FSharp solution with a counter grain, FsCheck property tests, and silo configuration — ready in under 2 minutes.

Orleans.FSharp

Core developer surface: grainContract, grainFor, journaledGrainFor, observers, streaming, logging, reminders, timers, and serialization.

Orleans.FSharp.Runtime

Hosting and client runtime: AddFunctionalGrain registration, siloConfig {} and clientConfig {} builders for production setup.

Orleans.FSharp.Abstractions

The fixed functional transport: request envelopes, protocol tokens, and precompiled Orleans proxies — reference it once, generate nothing.

Orleans.FSharp.EventSourcing

Compatibility package for applications covered by the separate Legacy API documentation. Current event-sourced actors use journaledGrainFor from the core package.

Orleans.FSharp.Testing

TestHarness, WebTestHarness, MockGrainFactory, GrainArbitrary, and log capture utilities.

Orleans.FSharp.Analyzers

Compile-time safety checks (for example OF0001 for async { }) with explicit opt-out where needed.

Every major Orleans capability, wrapped in idiomatic F#:

  • Grain lifecycle: activate, deactivate, per-stage onLifecycle hooks
  • State persistence: Memory, Redis, Azure Blob/Table, Cosmos, DynamoDB, ADO.NET
  • Streaming: Memory, Event Hubs, Azure Queue, broadcast channels — plus implicit subscriptions (onStream / onBroadcast) that activate a functional grain on publish
  • Streaming replies: API record fields returning IAsyncEnumerable<'T>, riding Orleans’ own extension — await foreach-able from C#
  • Reentrancy & concurrency: reentrant and per-request mayInterleave as contract operations; statelessWorker as a definition operation
  • Placement strategies: placement (PreferLocal, ActivationCountBased, ResourceOptimized, Random) as a definition operation
  • Event sourcing: journaledGrainFor — pure apply fold over Orleans’ LogStorage/StateStorage log-consistency providers, with typed custom snapshots
  • Transactions: distributed ACID via transactional contract operations and transactionalStateFrom facets
  • Contract versioning: exact by default; opt-in tolerance with acceptsVersions and per-operation sinceVersion
  • Observers & filters: codegen-free functional observers, incoming/outgoing call filters, request context propagation
  • TLS/mTLS: Certificate-based silo and client encryption
  • Health checks & OpenTelemetry: Production observability out of the box
  • Kubernetes clustering: native K8s support with namespace and clustering configuration