Skip to content

API Reference

Quick reference for the public modules, types, and functions in Orleans.FSharp.

Quick reference for the public modules, types, and functions in Orleans.FSharp.

Reference tables, not tutorials. Every section names the guide that carries the semantics; look there for what a thing means and here for what it is called.

The functional grain runtime is the current authoring model and comes first. Shared Orleans helpers follow it. The superseded authoring surface has its own Legacy API Reference.

Where the names in the functional tables come from. Every custom-operation name and every context member below is pinned by tests/Orleans.FSharp.Tests/FunctionalSurfaceTests.fs, which reflects over the builders and the context type and asserts the exact set. A name that appears here and not there, or there and not here, is a bug in one of the two.


The current grain authoring model. A user-authored API record instead of a C# CodeGen interface, a contract that declares the wire and delivery policy, and a definition that binds handlers to it. See Functional Grain Runtime for the full guide.

Entry pointSignatureDescription
grainContract<'Actor, 'Key, 'Api>GrainContractBuilder<'Actor,'Key,'Api> — a value, not a functionOpens the contract CE
contract<'Key, 'Api>GrainContractBuilder<'Api,'Key,'Api> — a value, not a functionShort form: the API record is its own actor brand (details)
grainFor contractGrainContract<...> -> FunctionalGrainDefinitionBuilder<...>Opens the definition CE
journaledGrainFor contractGrainContract<...> -> FunctionalJournaledGrainDefinitionBuilder<...>Opens the journaled definition CE (Event Sourcing)
observerContract<'Brand, 'Api>ObserverContractBuilder<'Brand,'Api> — a value, not a functionOpens the observer contract CE
FunctionalGrain.refcontract -> IGrainFactory -> 'Key -> 'ApiBinds a typed API record
FunctionalGrain.rawRefcontract -> IGrainFactory -> 'Key -> FunctionalGrainRef<'Actor,'Key,'Api>Binds the typed wrapper
FunctionalGrain.streamIdcontract -> string -> 'Key -> StreamIdStream id whose key is the contract’s own grain-key bytes
FunctionalGrain.channelIdcontract -> string -> 'Key -> ChannelIdThe same for a broadcast channel

The three contract entry points are type functions — generic values, not functions of unit. grainContract<RoomActor, RoomId, RoomApi> is the builder, so the CE braces follow the type arguments directly and there is no () to write. F# re-evaluates a type function at every mention, so each contract expression opens on its own builder instance (pinned by tests/Orleans.FSharp.Tests/FunctionalSurfaceTests.fs, “each entry point mention yields its own builder”). The other four rows are ordinary functions and take their argument as usual.

FunctionalGrain is a static class, so ref/rawRef generalize only where F# lets a static-class application generalize — see Functional grains, “The FunctionalGrain static-class inference rule”.

Contract builder — grainContract<'Actor, 'Key, 'Api> { }

Section titled “Contract builder — grainContract<'Actor, 'Key, 'Api> { }”
KeywordSignatureDescription
grainTypestringThe wire GrainType string — routing and storage identity. Optional; see Functional grains, “Optional grainType”
versionintContract version — matched exactly unless acceptsVersions widens it. Defaults to 1
stringKey / guidKey / int64KeyNative key codec: the domain key type is the Orleans key type
stringKeyMapped / guidKeyMapped / int64KeyMapped('Key -> K) (K -> 'Key)Mapped key codec over a domain key type
guidCompoundKey / int64CompoundKeyNative compound key (Guid/int64 + string extension)
guidCompoundKeyMapped / int64CompoundKeyMapped('Key -> K * string) (K -> string -> 'Key)Mapped compound key
readOnlyselectorThe handler’s returned state is discarded; interleaves with other read-only calls
oneWayselectorThe caller’s Task completes once the message enters the local send path
alwaysInterleaveselectorInterleaves regardless of readOnly/oneWay; also state-neutral. Rejected at sealing when the contract declares reentrant or mayInterleave
transactionalOrleans.TransactionOption -> selectorOrleans transaction policy for one operation (Functional grains, “Distributed ACID transactions”). Orleans’ own enum, not this library’s Orleans.FSharp.Transactions.TransactionOption DU
operationIdstring -> selectorOverride an operation’s wire ID, decoupling it from the F# field name. A second overload takes a StreamSelector
sinceVersionint -> selectorThe version an operation was introduced at; an admitted older call is refused for it by name. A second overload takes a StreamSelector
reentrantWhole-grain reentrancy — every request may enter a busy activation. Does not make whole-state replacement concurrency-safe
mayInterleave(IFunctionalRequestMetadata -> bool)Per-request interleave predicate over protocol metadata only; mutually exclusive with reentrant. Orleans consults it for the running request too
acceptsVersionsVersionPolicyExact (default) or BackwardCompatible n — which request versions this definition admits

operationId and sinceVersion are the only two per-operation declarations that compose with a streaming field; the four admission policies are refused at sealing.

Every API field takes exactly one F# argument. Prefer a named record for multi-input domain data (typing: Typing -> Task<unit>); tuples remain valid when positional data is intentional. A field spelled curried fails contract construction. See Functional grains, “One operation, one argument”.

Definition builder — grainFor contract { }

Section titled “Definition builder — grainFor contract { }”
KeywordHandler signatureDescription
defaultStateunit -> 'StateEphemeral state factory, called once per activation
initialState'Key -> 'StateKey-aware ephemeral state factory
handleselector + Handler<'Actor,'Key,'State,'Arg,'Reply>Attach a handler to one API operation
handleQueryselector + QueryHandler<'Actor,'Key,'State,'Arg,'Reply>Attach a reply-only handler; the operation must be declared readOnly
handleStreamstreamSelector + StreamHandler<'Actor,'Key,'State,'Arg,'Item>Attach a handler to one server-streaming operation (Streaming replies)
stateFromPersistentStateRef<'State>Attach the primary persistent-state holder
usePersistentStatePersistentStateRef<'S> + ('Key -> 'S)Attach an additional named persistent-state facet (repeatable)
transactionalStateFromTransactionalStateRef<'S> + ('Key -> 'S)Attach a transactional facet (repeatable)
collectionAgeTimeSpanIdle-deactivation threshold override
placementPlacementStrategyRandom / PreferLocal / ActivationCountBased / ResourceOptimized
statelessWorkerintStateless-worker placement with a max-local-workers cap
onActivateActivateHook<'Actor,'Key,'State>Activation hook; its returned state is published in memory
onDeactivateDeactivateHook<'Actor,'Key,'State>Deactivation hook; no replacement state
onLifecycleLifecycleStage + LifecycleHook<'Actor,'Key>Hook a numbered Orleans grain-lifecycle stage
onReminderstring + TimeSpan (due) + TimeSpan (period) + ReminderHook<...>Declare a reminder
onTimerstring + GrainTimerCreationOptions + TimerHook<...>Declare a timer
onStreamstring (provider) + string (namespace) + StreamHook<...>Implicit stream subscription
onBroadcaststring (provider) + string (namespace) + StreamHook<...>Implicit broadcast-channel subscription

Journaled definition builder — journaledGrainFor contract { }

Section titled “Journaled definition builder — journaledGrainFor contract { }”

A journal-aware version of the operations above: request, timer, reminder, stream, and broadcast handlers return events instead of replacement state. A journal still cannot be a transaction participant or be shared by the many activations of a stateless worker. See Event Sourcing.

KeywordHandler signatureDescription
initialEventState'Key -> 'StateThe seed the journal folds onto. Required, and first
apply'State -> 'Event -> 'StateThe pure fold. Required, and second — it introduces the event type
logProviderstringThe registered log-consistency provider. Required
journalStoragestringThe grain storage a built-in provider writes through; defaults to the silo’s default IGrainStorage and cannot be combined with customStorage
customStorageIServiceProvider -> IFunctionalJournalStorage<'Key,'State,'Event>Typed storage bridge for Orleans’ CustomStorage provider
snapshotPolicyFunctionalJournalSnapshotPolicy<'State>Per-definition Inherit, Disabled, Every n, or When override; requires customStorage
handleselector + JournaledHandler<'Actor,'Key,'State,'Event,'Arg,'Reply>A handler returning events, reply
handleQueryselector + QueryHandler<'Actor,'Key,'State,'Arg,'Reply>A reply-only handler that raises nothing; the operation must be declared readOnly
handleStreamstreamSelector + StreamHandler<...>A streaming operation; raises no events
onActivateJournaledActivateHook<'Actor,'Key,'State>Runs after replay; returns no state
onDeactivateJournaledDeactivateHook<'Actor,'Key,'State>Deactivation hook
onReminderstring + due/period + JournaledReminderHook<...>A successful tick appends and confirms returned events
onTimerstring + GrainTimerCreationOptions + JournaledTimerHook<...>Appends returned events; Interleave = true is supported
onStreamprovider + namespace + JournaledStreamHook<...>Implicit stream delivery appending returned events
onBroadcastprovider + namespace + JournaledStreamHook<...>Implicit broadcast delivery appending returned events
onTentativeStateChangedJournaledStateChangedHook<...>Synchronous tentative-view notification
onStateChangedJournaledStateChangedHook<...>Synchronous confirmed-view notification
onConnectionIssueJournaledConnectionIssueHook<...>Synchronous Orleans connection-issue notification
onConnectionIssueResolvedJournaledConnectionIssueHook<...>Synchronous recovery notification
collectionAgeTimeSpanIdle-deactivation threshold override
placementPlacementStrategyAs above. statelessWorker has no journaled form at all: many activations of one grain cannot share a journal

FunctionalGrainContext<'Actor, 'Key> — the per-invocation context

Section titled “FunctionalGrainContext<'Actor, 'Key> — the per-invocation context”

Passed to every handler, hook, timer, reminder, and stream callback.

MemberTypeDescription
key'KeyThe domain key decoded from the grain identity
grainIdGrainIdThe Orleans identity of this activation
grainFactoryIGrainFactoryBind further grain references
servicesIServiceProviderResolve DI services registered on the silo
loggerILoggerLogger scoped to this activation
timeProviderTimeProviderThe registered time provider
utcNowDateTimeOffsetFrozen at context creation — stable across the whole callback
cancellationTokenCancellationTokenSelected by callback kind
streamSequenceTokenStreamSequenceToken optionThe delivery cursor; Some only inside an onStream delivery on a rewindable provider
deactivateOnIdle()unit -> unitRequest deactivation once this turn ends
delayDeactivation(span)TimeSpan -> unitPostpone idle collection
persistentState(ref)PersistentStateRef<'S> -> IPersistentState<'S>Look up an attached persistent-state facet
transactionalState(ref)TransactionalStateRef<'S> -> FunctionalTransactionalState<'S>Look up an attached transactional facet
journalVersionintThe confirmed journal length, as it was when the turn started
journalState<'S>()unit -> 'SCurrent confirmed view
journalTentativeState<'S>()unit -> 'SConfirmed view plus submitted events
unconfirmedEvents<'E>()unit -> 'E listLocally submitted, unconfirmed suffix
raiseEvent(event) / raiseEvents(events)'E -> unit / 'E list -> unitSubmit without waiting for confirmation
confirmEvents()unit -> TaskConfirm all submitted entries
snapshotNow()unit -> unitForce a custom-storage snapshot after this successful callback’s events; overrides disabled automatic rules
refreshJournal()unit -> TaskConfirm all submitted events and synchronize the confirmed view with the global journal
retrieveConfirmedEvents<'E>(from, to)int * int -> Task<'E list>Read a provider-supported half-open event segment
clearJournal()unit -> TaskClear the whole log and restore the initial state
enableJournalStats() / disableJournalStats()unit -> unitToggle Orleans log-consistency statistics
getJournalStats()unit -> LogConsistencyStatisticsRead collected statistics
raiseConditional(events)'Event list -> Task<bool>Append and confirm inside the turn; reports whether it was accepted
raiseConditionalEvent(event)'Event -> Task<bool>Single-event conditional append
tryGetRequestContext<'T>(name)string -> 'T optionTyped Orleans request-context read
setRequestContext(name, value)string -> 'V -> unitRequest-context write
removeRequestContext(name)string -> unitRequest-context removal

The journal members live on the one context type rather than on a journaled variant of it, and all refuse with a definition-stage diagnostic on an ordinary grainFor definition.

FunctionalGrainRef<'Actor, 'Key, 'Api> — the bound reference

Section titled “FunctionalGrainRef<'Actor, 'Key, 'Api> — the bound reference”
MemberSignatureDescription
key'KeyThe domain key this reference is bound to
api'ApiThe bound API record; the same instance on every access
callselector -> 'Arg -> Task<'Reply>Invoke one operation by selector
callCancellableselector -> 'Arg -> CancellationToken -> Task<'Reply>The same, with a token
streamstreamSelector -> 'Arg -> IAsyncEnumerable<'Item>Invoke one streaming operation
streamCancellablestreamSelector -> 'Arg -> CancellationToken -> IAsyncEnumerable<'Item>The same, with a token
TypeDefinition
Handler<'Actor,'Key,'State,'Argument,'Reply>context -> 'State -> 'Argument -> Task<'State * 'Reply>
QueryHandler<'Actor,'Key,'State,'Argument,'Reply>context -> 'State -> 'Argument -> Task<'Reply> — what handleQuery binds, on both definition builders
StreamHandler<'Actor,'Key,'State,'Argument,'Item>context -> 'State -> 'Argument -> IAsyncEnumerable<'Item>
JournaledHandler<'Actor,'Key,'State,'Event,'Argument,'Reply>context -> 'State -> 'Argument -> Task<'Event list * 'Reply>
ActivateHook<'Actor,'Key,'State>context -> 'State -> Task<'State>
DeactivateHook<'Actor,'Key,'State>context -> DeactivationReason -> 'State -> Task<unit>
JournaledActivateHook<'Actor,'Key,'State>context -> 'State -> Task<unit>
JournaledDeactivateHook<'Actor,'Key,'State>context -> DeactivationReason -> 'State -> Task<unit>
JournaledReminderHook<'Actor,'Key,'State,'Event>context -> 'State -> TickStatus -> Task<'Event list>
JournaledTimerHook<'Actor,'Key,'State,'Event>context -> 'State -> Task<'Event list>
JournaledStreamHook<'Actor,'Key,'State,'Event,'Item>context -> 'State -> 'Item -> Task<'Event list>
JournaledStateChangedHook<'Actor,'Key,'State>context -> 'State -> unit
JournaledConnectionIssueHook<'Actor,'Key,'State>context -> 'State -> ConnectionIssue -> unit
ReminderHook<'Actor,'Key,'State>context -> 'State -> TickStatus -> Task<'State>
TimerHook<'Actor,'Key,'State>context -> 'State -> Task<'State>
StreamHook<'Actor,'Key,'State,'Item>context -> 'State -> 'Item -> Task<'State>
LifecycleHook<'Actor,'Key>context -> Task<unit>
OperationSelector<'Api,'Argument,'Reply>'Api -> ('Argument -> Task<'Reply>) — a field projection of a unary operation (_.join)
StreamSelector<'Api,'Argument,'Item>'Api -> ('Argument -> IAsyncEnumerable<'Item>) — a field projection of a streaming operation (_.tail)
FunctionSignatureDescription
PersistentState.create<'State>string -> string -> PersistentStateRef<'State>stateName -> providerName -> descriptor

The descriptor’s (stateName, providerName, storedType) triple is its logical identity, and it is durable identity — see Functional grains, “Persistence model”.

NameSignatureDescription
TransactionalState.create<'State>string -> string -> TransactionalStateRef<'State>stateName -> storageName -> descriptor
FunctionalTransactionalState<'S>.readunit -> Task<'S>The current value, copied before it is returned
FunctionalTransactionalState<'S>.readWith('S -> 'R) -> Task<'R>A projection, run inside Orleans’ read lock and returned uncopied
FunctionalTransactionalState<'S>.update('S -> 'S) -> Task<unit>Replace the value, inside Orleans’ write lock
FunctionalTransactionalState<'S>.updateWith('S -> 'S * 'R) -> Task<'R>Replace and return a result

Both update functions are synchronous by type: Orleans runs them inside the transactional state’s reader-writer lock and rejects re-entering the same state from inside a callback.

NameSignatureDescription
handleStreamsee the definition builderBinds a streaming operation
FunctionalGrainRef.stream / .streamCancellablesee the bound referenceCalls one by selector
FunctionalStream.withBatchSizeint -> IAsyncEnumerable<'T> -> IAsyncEnumerable<'T>Set the pull batch size of a functional stream call

A streaming field is 'Arg -> IAsyncEnumerable<'Item>, not 'Arg -> Task<...>; that is what makes it a second field kind rather than an ordinary operation. See Streaming replies.

A handler record whose every field is 'Msg -> Task<unit>. Push to a client-hosted observer with no application code generation; see Functional grains, “Push to clients: functional observers”.

KeywordSignatureDescription
observerTypestringWire identity of the observer; defaults to the brand’s simple CLR name, which requires a simple, non-generic, non-nested brand exactly as a derived grainType does
versionintContract version; defaults to 1

A push operation’s wire ID is always its handler-record field name — there is no operationId override, so the notifying and observing sides cannot drift apart.

FunctionSignatureDescription
createObserverContract -> IClusterClient -> 'Api -> FunctionalObserverHandle<'Brand,'Api>Host a handler record and return a serializable typed handle
createFromObserverContract -> IServiceProvider -> 'Api -> FunctionalObserverHandle<'Brand,'Api>The same, from any services carrying the functional transport (e.g. inside a silo)
notifyhandle -> selector -> 'Msg -> Task<unit>Push one message; resolves its selector on every call — the convenience form
notifierhandle -> selector -> ('Msg -> Task<unit>)Resolve once, return a preclosed push function — the hot-path form
unsubscribeIGrainFactory -> handle -> unitRelease the object reference; idempotent
MemberSignatureDescription
.ctorTimeSpanLiveness window a subscription must be refreshed within
Subscribehandle -> unitAdd or refresh a subscription
Unsubscribehandle -> boolRemove one subscription
Notifyselector -> 'Msg -> Task<unit>Fan out to every live subscription; resolves its selector once per call, not once per subscriber
RemoveExpiredunit -> unitDrop subscriptions past the liveness window
Clearunit -> unitForget every subscription
CountintLive subscription count
ExpiryTimeSpanThe configured liveness window

A manager is a mutable object held in ephemeral handler state. It holds live object references, so it must never be part of a persistent state type — the F# codec refuses one.

TypeDescription
GrainContract<'Actor, 'Key, 'Api>Sealed result of grainContract { }
FunctionalGrainDefinition<'Actor, 'Key, 'Api, 'State>Sealed result of grainFor { }
FunctionalJournaledGrainDefinition<'Actor, 'Key, 'Api, 'State, 'Event>Sealed result of journaledGrainFor { }
IFunctionalJournalStorage<'Key, 'State, 'Event>Typed read/append/clear contract behind Orleans’ CustomStorage provider
FunctionalJournalStorageIdentity<'Key>Grain type, complete GrainId, and decoded key supplied to custom storage
FunctionalJournalRead<'State, 'Event>Optional snapshot plus the ordered retained event tail
FunctionalJournalWrite<'State, 'Event>CAS version, atomic event batch, and optional resulting snapshot
FunctionalJournalSnapshot<'State>Materialized state plus the event version it represents
FunctionalJournalSnapshotPolicy<'State>Per-definition Inherit, Disabled, Every, or typed When rule
FunctionalJournalSnapshotDefaultSilo-wide Disabled, Every, or heterogeneous When rule
FunctionalJournalSnapshotOptionsOptions whose Policy is inherited by custom-storage definitions
FunctionalJournalSnapshotContextBoxed identity, version, state type, and state passed to a global When rule
FunctionalJournalPermanentStorageExceptionMarks a custom-storage failure as non-retryable; the runtime fails the operation and deactivates the grain
FunctionalGrainContext<'Actor, 'Key>Per-invocation context (members above)
FunctionalGrainRef<'Actor, 'Key, 'Api>Typed reference wrapper (members above)
ObserverContract<'Brand, 'Api>Sealed result of observerContract { }; exposes ObserverTypeName and Version
FunctionalObserverHandle<'Brand, 'Api>Serializable typed handle to a client-hosted observer; an operation argument or a tuple element, never an F# record field
PersistentStateRef<'State>Immutable descriptor returned by PersistentState.create
TransactionalStateRef<'State>Immutable descriptor returned by TransactionalState.create
FunctionalTransactionalState<'State>The invocation-bound transactional facade
PlacementStrategyRandom, PreferLocal, ActivationCountBased, ResourceOptimized
VersionPolicyExact, BackwardCompatible of int
LifecycleStageFirst, SetupState, Activate, Last (Activate is rejected by onLifecycle; use onActivate)
IFunctionalRequestMetadatamayInterleave’s argument: GrainType, ContractVersion, OperationId, IsReadOnly, IsOneWay, IsAlwaysInterleave, PayloadLength
FunctionalGrainTransportOptionsTransport limits; DefaultMaxPayloadBytes is 16 MiB

These are the complete application-facing members of the typed CustomStorage bridge. The storage implementation, not the runtime, owns durable I/O; the runtime owns replay through the definition’s single apply fold.

MemberSignatureContract
IFunctionalJournalStorage.ReadFunctionalJournalStorageIdentity<'Key> -> Task<FunctionalJournalRead<'State,'Event>>Return the latest snapshot and the ordered retained tail strictly after it
IFunctionalJournalStorage.AppendFunctionalJournalStorageIdentity<'Key> * FunctionalJournalWrite<'State,'Event> -> Task<bool>Compare-and-swap on ExpectedVersion; append the batch and optional snapshot atomically, or return false without changing storage
IFunctionalJournalStorage.ClearFunctionalJournalStorageIdentity<'Key> -> TaskDelete the complete journal for that identity
TypePublic members
FunctionalJournalStorageIdentity<'Key>GrainTypeName: string, GrainId: GrainId, Key: 'Key
FunctionalJournalSnapshot<'State>Version: int, State: 'State
FunctionalJournalRead<'State,'Event>Snapshot: FunctionalJournalSnapshot<'State> option, Events: IReadOnlyList<'Event>
FunctionalJournalWrite<'State,'Event>ExpectedVersion: int, Events: IReadOnlyList<'Event>, Snapshot: FunctionalJournalSnapshot<'State> option
FunctionalJournalSnapshotPolicy<'State>`Inherit
FunctionalJournalSnapshotDefault`Disabled
FunctionalJournalSnapshotContextGrainTypeName: string, GrainId: GrainId, Key: obj, Version: int, StateType: Type, State: obj
FunctionalJournalSnapshotOptionsMutable Policy; mutable ManualSnapshotMaxConflictRetries (default 3, must be >= 0, counts retries after the first CAS attempt)
FunctionalJournalPermanentStorageExceptionConstructors (message: string) and (message: string, innerException: Exception)

Ordinary exceptions raised while Orleans’ CustomStorage adaptor reads or appends events are considered transient and remain eligible for its retry loop. A zero-event manual snapshot and Clear call the typed store directly: an ordinary exception fails that call once without deactivation, and the caller may retry explicitly. Throw FunctionalJournalPermanentStorageException only when the same operation cannot succeed without an application, configuration, or durable-data change. The functional runtime exits that retry loop, fails the current journal operation, and requests deactivation so a later call starts with a fresh activation and durable read. The permanent exception also fails and deactivates on both direct paths.

Snapshot resolution is deterministic: context.snapshotNow() for the successful callback wins; otherwise the definition’s snapshotPolicy wins; Inherit or no definition policy uses FunctionalJournalSnapshotOptions.Policy; the silo default is Disabled. These policies apply only to definitions with customStorage. See Event Sourcing.

ManualSnapshotMaxConflictRetries applies to a zero-event manual snapshot requested through context.snapshotNow(). The default 3 permits the initial compare-and-swap attempt plus three retries. Each retry refreshes durable state and recomputes the snapshot; 0 permits only the first attempt, and exhausting the limit fails the call without writing the snapshot.

MethodSignatureDescription
AddFunctionalGrainISiloBuilder -> FunctionalGrainDefinition<...> -> ISiloBuilderRegister a hosted definition (Orleans.FSharp.Runtime)
AddFunctionalJournaledGrainISiloBuilder -> FunctionalJournaledGrainDefinition<...> -> ISiloBuilderRegister a hosted journaled definition (Orleans.FSharp.Runtime)
ConfigureFunctionalJournalSnapshotsISiloBuilder * Action<FunctionalJournalSnapshotOptions> -> ISiloBuilderConfigure the silo-wide rule inherited by custom-storage definitions
UseFunctionalJournalSnapshotsISiloBuilder * every:int -> ISiloBuilderSet a positive fixed event-count default
AddFunctionalGrainClientIClientBuilder -> IClientBuilderRegister the client-side transport on a client-only process (Orleans.FSharp)

Both silo registrations install the client transport too, and both are idempotent per definition value. A standalone F# host also has to make Orleans see the assemblies it reaches only through F# — see Functional grains, “Running a silo from a standalone F# process”.

NameSignatureDescription
FunctionalGrainRegistration.of'FunctionalGrainDefinition<...> -> FunctionalGrainRegistrationErase an ordinary definition’s four type parameters so a heterogeneous list can be passed around
FunctionalScripting.startOnPortsint -> int -> FunctionalGrainRegistration list -> Task<Scripting.SiloHandle>Start a one-line localhost silo hosting those definitions, manifest pre-load included
Scripting.startOnPortsint -> int -> Task<SiloHandle>The same without functional definitions (Orleans.FSharp)
Scripting.shutdownSiloHandle -> Task<unit>Stop the silo
NameSignatureDescription
FunctionalGrainInterop.For<'TFacade>FunctionalContract * IGrainFactory * obj -> 'TFacadeBind a C#-declared facade interface to a functional contract
FunctionalOperationAttribute.ctor(string), OperationIdMap a facade method to a wire operation ID that differs from its name

The facade names no definition kind: an ordinary and a journaled definition are indistinguishable across the boundary. See Calling from C#.


Shared Orleans helpers which compose with functional definitions and hosting code.

TypeDescription
CompoundGuidKeyCompound key: GUID + string extension
CompoundIntKeyCompound key: int64 + string extension
Immutable<'T>Alias for Orleans.Concurrency.Immutable<'T> for zero-copy passing
FSharpIncomingFilterWraps an F# function as IIncomingGrainCallFilter
FSharpOutgoingFilterWraps an F# function as IOutgoingGrainCallFilter
Migration<'TOld, 'TNew>State migration definition from one version to another
AssemblyMarkerMarker type for assembly discovery
FunctionSignatureDescription
incoming(IIncomingGrainCallContext -> Task<unit>) -> IIncomingGrainCallFilterCreate incoming filter
outgoing(IOutgoingGrainCallContext -> Task<unit>) -> IOutgoingGrainCallFilterCreate outgoing filter
incomingWithAroundbefore -> after -> IIncomingGrainCallFilterBefore/after incoming filter
outgoingWithAroundbefore -> after -> IOutgoingGrainCallFilterBefore/after outgoing filter

Filters see a functional grain as an ordinary Orleans call — see Functional grains, “Call filters over a functional grain”.

FunctionSignatureDescription
methodNameIIncomingGrainCallContext -> stringGet called method name
interfaceTypeIIncomingGrainCallContext -> TypeGet grain interface type
grainInstanceIIncomingGrainCallContext -> obj optionGet grain instance
FunctionSignatureDescription
setstring -> obj -> unitSet a request context value
get<'T>string -> 'T optionGet a typed context value
getOrDefault<'T>string -> 'T -> 'TGet with fallback
removestring -> unitRemove a context value
withValue<'T>string -> obj -> (unit -> Task<'T>) -> Task<'T>Scoped context value
FunctionSignatureDescription
logInfoILogger -> string -> obj[] -> unitLog informational message
logWarningILogger -> string -> obj[] -> unitLog warning message
logErrorILogger -> exn -> string -> obj[] -> unitLog error with exception
logDebugILogger -> string -> obj[] -> unitLog debug message
withCorrelationstring -> (unit -> Task<'T>) -> Task<'T>Scoped correlation ID
currentCorrelationIdunit -> string optionGet current correlation ID
FunctionSignatureDescription
configureGracefulShutdownTimeSpan -> IHostBuilder -> IHostBuilderSet drain timeout
stopHostIHost -> Task<unit>Stop host gracefully
onShutdown(CT -> Task<unit>) -> IHostBuilder -> IHostBuilderRegister shutdown handler
FunctionSignatureDescription
migration<'TOld, 'TNew>int -> int -> ('TOld -> 'TNew) -> Migration<obj, obj>Define a migration. The result is erased to Migration<obj, obj> so a chain over several state versions is one homogeneous list
applyMigrations<'T>Migration<obj, obj> list -> int -> obj -> 'TApply migration chain (throws on invalid chain)
tryApplyMigrations<'T>Migration<obj, obj> list -> int -> obj -> Result<'T, string list>Validate and apply; returns Ok or Error with messages
validateMigration<obj, obj> list -> string listValidate migration chain; empty list means valid
FunctionSignatureDescription
fsharpJsonOptionsJsonSerializerOptionsPre-configured F# JSON options
addFSharpConvertersJsonSerializerOptions -> JsonSerializerOptionsAdd F# converters
withConvertersJsonConverter list -> JsonSerializerOptionsCreate options with extras
FunctionSignatureDescription
taskResult'T -> Task<Result<'T, 'E>>Wrap as Ok
taskError'E -> Task<Result<'T, 'E>>Wrap as Error
taskMap('T -> 'U) -> Task<Result<'T, 'E>> -> Task<Result<'U, 'E>>Map Ok value
taskBind('T -> Task<Result<'U, 'E>>) -> Task<Result<'T, 'E>> -> Task<Result<'U, 'E>>Bind Ok value

GrainResilience — Polly v8 resilience wrappers

Section titled “GrainResilience — Polly v8 resilience wrappers”

Wrap any grain call in retry, circuit-breaker, and timeout strategies. See Resilience guide.

TypeDescription
ResilienceOptionsRecord: MaxRetryAttempts, RetryDelay, CircuitBreakerThreshold, CircuitBreakerDuration, Timeout
FunctionSignatureDescription
GrainResilience.defaultOptionsResilienceOptions3 retries · 1s delay · no circuit breaker · no timeout
GrainResilience.retry<'T>int -> TimeSpan -> (unit -> Task<'T>) -> Task<'T>Retry N times with delay; each attempt re-invokes the call
GrainResilience.withTimeout<'T>TimeSpan -> (unit -> Task<'T>) -> Task<'T>Deadline on one call — raises TimeoutRejectedException and abandons the in-flight call (does not cancel it)
GrainResilience.withTimeoutCancellable<'T>TimeSpan -> (CancellationToken -> Task<'T>) -> Task<'T>Same deadline, handed to the operation as a token so it can stop instead of being abandoned
GrainResilience.execute<'T>ResilienceOptions -> (unit -> Task<'T>) -> Task<'T>Full options: retry + circuit breaker + timeout. The timeout spans the whole sequence; the pipeline is rebuilt per call, so circuit state is not shared
GrainResilience.executeCancellable<'T>ResilienceOptions -> (CancellationToken -> Task<'T>) -> Task<'T>Full options for an operation that takes the deadline’s token
GrainResilience.buildPipeline<'T>ResilienceOptions -> ResiliencePipeline<'T>Build reusable Polly pipeline — the way to get shared circuit state
GrainResilience.circuitBreakerint -> TimeSpan -> ResiliencePipelineShared circuit breaker (non-generic, long-lived)
FunctionSignatureDescription
GrainBatch.map<'TG,'TR>'TG seq -> ('TG -> Task<'TR>) -> Task<'TR list>Fan-out; fails if any call throws
GrainBatch.tryMap<'TG,'TR>'TG seq -> ('TG -> Task<'TR>) -> Task<Result<'TR, exn> list>Fan-out; captures individual failures
GrainBatch.aggregate<'TG,'TR,'TA>'TG seq -> ('TG -> Task<'TR>) -> ('TR list -> 'TA) -> Task<'TA>Fan-out then reduce
GrainBatch.iter<'TG>'TG seq -> ('TG -> Task) -> TaskConcurrent fan-out; waits for every call and fails if any throws
GrainBatch.tryIter<'TG>'TG seq -> ('TG -> Task) -> Task<Result<unit, exn> list>Concurrent fan-out; waits for every call and captures failures
GrainBatch.choose<'TG,'TR>'TG seq -> ('TG -> Task<'TR option>) -> Task<'TR list>Fan-out; filters out None results
GrainBatch.partition<'TG,'TR>'TG seq -> ('TG -> Task<'TR>) -> Task<'TR list * exn list>Fan-out; separates successes from failures

Tip: For 2–4 fixed grain calls, prefer the F# and! applicative keyword inside task {} — it is more ergonomic and starts every bound call before awaiting any of them, exactly as these do. Use GrainBatch when the number of grains is dynamic.

ModuleKey FunctionDescription
FSharpSerialization.addFSharpSerializationISiloBuilder -> ISiloBuilderOrleans native F# serializer
FSharpBinaryCodecRegistration.addToSerializerBuilderISerializerBuilder -> ISerializerBuilderRegister FSharpBinaryCodec manually
immutable'T -> Immutable<'T>Wrap as immutable
unwrapImmutableImmutable<'T> -> 'TUnwrap immutable

TypeDescription
StreamRef<'T>Typed reference to an Orleans stream (Provider, StreamId)
StreamSubscription<'T>Active stream subscription handle (Handle)
FunctionSignatureDescription
getStream<'T>IStreamProvider -> string -> string -> StreamRef<'T>Get stream reference
publish<'T>StreamRef<'T> -> 'T -> Task<unit>Publish event
subscribe<'T>StreamRef<'T> -> ('T -> Task<unit>) -> Task<StreamSubscription<'T>>Subscribe with callback
subscribeWithToken<'T>StreamRef<'T> -> ('T -> StreamSequenceToken option -> Task<unit>) -> Task<StreamSubscription<'T>>Subscribe with the event’s cursor — the way to checkpoint for subscribeFrom
asTaskSeq<'T>StreamRef<'T> -> TaskSeq<'T>Pull-based consumption
subscribeFrom<'T>StreamRef<'T> -> StreamSequenceToken -> ('T -> Task<unit>) -> Task<StreamSubscription<'T>>Subscribe from token (rewind is inclusive of that event)
subscribeFromWithToken<'T>StreamRef<'T> -> StreamSequenceToken -> ('T -> StreamSequenceToken option -> Task<unit>) -> Task<StreamSubscription<'T>>Rewind and keep checkpointing
unsubscribe<'T>StreamSubscription<'T> -> Task<unit>Cancel subscription
getSubscriptions<'T>StreamRef<'T> -> Task<StreamSubscription<'T> list>List subscriptions
resumeAll<'T>StreamRef<'T> -> ('T -> Task<unit>) -> Task<unit>Resume all subscriptions
getSequenceToken<'T>StreamSubscription<'T> -> StreamSequenceToken optionDeprecated — carries [<Obsolete>] (a warning, not an error) and still returns always None: StreamSubscriptionHandle exposes no token, so there was never anything to return. Replacement: subscribeWithToken / subscribeFromWithToken, or context.streamSequenceToken in an onStream hook

A functional definition consumes a stream declaratively with onStream instead; see Streaming and Functional grains, “Implicit subscriptions”.


TypeDescription
BroadcastChannelRef<'T>Typed reference to a broadcast channel
FunctionSignatureDescription
getChannel<'T>IBroadcastChannelProvider -> string -> string -> BroadcastChannelRef<'T>Get channel reference
publish<'T>BroadcastChannelRef<'T> -> 'T -> Task<unit>Publish to all subscribers

FunctionSignatureDescription
addEventHubStreamsstring -> string -> string -> ISiloBuilder -> ISiloBuilderEvent Hubs provider
addAzureQueueStreamsstring -> string -> ISiloBuilder -> ISiloBuilderAzure Queue provider
addRedisStreamsstring -> string -> ISiloBuilder -> ISiloBuilderRedis Streams provider (experimental: needs a prerelease Microsoft.Orleans.Streaming.Redis)

TypeDescription
GrainDirectoryProviderDefault, Redis, AzureStorage, Custom
FunctionSignatureDescription
configureGrainDirectoryProvider -> ISiloBuilder -> ISiloBuilderConfigure grain directory

FunctionSignatureDescription
useKubernetesClusteringISiloBuilder -> ISiloBuilderEnable K8s clustering
useKubernetesClusteringWithNamespacestring -> ISiloBuilder -> ISiloBuilderK8s with custom namespace

TypeDescription
SiloConfigImmutable silo configuration record
ClientConfigImmutable client configuration record
ClusteringModeLocalhost, RedisClustering, AzureTableClustering, AdoNetClustering, CustomClustering
ClientClusteringModeLocalhost, StaticGateway, Custom
StorageProviderMemory, RedisStorage, AzureBlobStorage, AzureTableStorage, AdoNetStorage, CosmosStorage, DynamoDbStorage, CustomStorage
StreamProviderMemoryStream, PersistentStream, CustomStream
ReminderProviderMemoryReminder, RedisReminder, CustomReminder
TlsConfigTlsSubject, TlsCertificate, MutualTlsSubject, MutualTlsCertificate
DashboardConfigDashboardDefaults, DashboardWithOptions
CEBuilderDescription
siloConfig { }SiloConfigBuilderConfigure an Orleans silo
clientConfig { }ClientConfigBuilderConfigure an Orleans client

See Silo configuration and Client configuration for the full keyword lists.

FunctionSignatureDescription
DefaultSiloConfigEmpty default configuration
validateSiloConfig -> string listValidate configuration
applyToSiloBuilderSiloConfig -> ISiloBuilder -> unitApply to silo builder
applyToHostSiloConfig -> HostApplicationBuilder -> unitApply to host

Both applyTo* entry points force the manifest pre-load a standalone F# host needs.

FunctionSignatureDescription
DefaultClientConfigEmpty default configuration
validateClientConfig -> string listValidate configuration
applyToBuilderClientConfig -> IClientBuilder -> unitApply to client builder
applyToHostClientConfig -> HostApplicationBuilder -> unitApply to host
buildClientConfig -> IHost * IClusterClientBuild and return client

TypeDescription
TestHarnessCluster, Client, LogFactory — a TestCluster with log capture
WebTestHarnessThe same plus an HttpClient against a live web host
WebUnitTestHarnessHttpClient + LogFactory, no cluster
MockGrainFactoryMock IGrainFactory for unit tests
CapturingLogger / CapturingLoggerFactoryIn-memory ILogger and its factory
CapturedLogEntryTimestamp, Level, Template, Properties, Exception
FunctionSignatureDescription
createTestClusterunit -> Task<TestHarness>Create default test cluster
createTestClusterWithSiloConfig -> Task<TestHarness>Create with custom config
getGrainByString<'T>TestHarness -> string -> GrainRef<'T, string>Get grain by string key
getGrainByInt64<'T>TestHarness -> int64 -> GrainRef<'T, int64>Get grain by int64 key
getGrainByGuid<'T>TestHarness -> Guid -> GrainRef<'T, Guid>Get grain by GUID key
captureLogsTestHarness -> CapturedLogEntry listGet all captured logs
resetTestHarness -> Task<unit>Clear captured logs
disposeTestHarness -> Task<unit>Stop and dispose cluster
FunctionSignatureDescription
create(ISiloBuilder -> unit) -> (IWebHostBuilder -> unit) -> Task<WebTestHarness>Cluster + web host
createDefault(IWebHostBuilder -> unit) -> Task<WebTestHarness>Default cluster + web host
createWithFactoryIGrainFactory -> (IWebHostBuilder -> unit) -> Task<WebUnitTestHarness>Web host over a supplied factory
createWithMockFactory(MockGrainFactory -> MockGrainFactory) -> (IWebHostBuilder -> unit) -> Task<WebUnitTestHarness>Web host over a mock factory
captureLogs / captureUnitLogsharness -> CapturedLogEntry listCaptured logs
reset / resetUnit, dispose / disposeUnitharness -> Task<unit>Reset and teardown
FunctionSignatureDescription
createunit -> MockGrainFactoryCreate empty mock factory
withGrain<'T>obj -> 'T -> MockGrainFactory -> MockGrainFactoryRegister a mock grain implementation
FunctionSignatureDescription
forState<'T>unit -> Arbitrary<'T>Auto-generate Arbitrary for state type
forCommands<'T>unit -> Arbitrary<'T list>Auto-generate Arbitrary for command sequences
FunctionSignatureDescription
commandSequenceArb<'T>unit -> Arbitrary<'T list>Non-empty command list Arbitrary
stateMachineProperty'State -> ('State -> 'Cmd -> 'State) -> ('State -> bool) -> 'Cmd list -> boolState machine invariant check
FunctionSignatureDescription
createunit -> CapturingLoggerFactoryCreate capturing factory
captureLogsCapturingLoggerFactory -> CapturedLogEntry listGet all entries

A functional definition is tested against a real TestCluster rather than a mock factory — see Testing.


Compile-time F# analyzer package — install in your grain projects to catch async {} misuse at build time.

Terminal window
dotnet add package Orleans.FSharp.Analyzers
CodeSeverityMessageDescription
OF0001WarningUse task { } instead of async { }Detects async { } computation expressions in Orleans grain code
TypeDescription
AllowAsyncAttributeSuppresses OF0001 on the annotated binding. Apply when async { } is genuinely required (e.g., interop with Async<'T> APIs).
// Triggers OF0001 — use task { } in grain handlers
let invalidWork () =
async { return 0 } // ⚠️ OF0001
// Suppress when async is genuinely needed
open Orleans.FSharp.Analyzers.AsyncUsageAnalyzer
[<AllowAsync>]
let allowedInterop () =
async { return 0 } // ✅ suppressed

See Analyzers guide for full documentation.

The original authoring surface is retained in the separate Legacy API Reference.