Skip to content

Grain extensions

Grain extensions attach an additional callable interface to grain activations. Orleans uses extensions internally for features such as cancellation and streaming. Applications can use them for infrastructure behavior that should be available across many grain types.

Extensions are an advanced integration mechanism. Prefer a normal grain interface when the behavior is part of the grain’s domain contract.

The interface derives from IGrainExtension and uses normal grain method return types:

public interface IDiagnosticsExtension : IGrainExtension
{
ValueTask<string> GetStatus();
}
public sealed class DiagnosticsExtension(
IGrainContext grainContext) : IDiagnosticsExtension
{
public ValueTask<string> GetStatus()
{
return ValueTask.FromResult(
$"Active grain: {grainContext.GrainId}");
}
}

Orleans generates extension request and reference code at build time.

Register a default implementation on the silo:

siloBuilder.AddGrainExtension<
IDiagnosticsExtension,
DiagnosticsExtension>();

The implementation is created through dependency injection for the target grain context.

Cast an existing grain reference to the extension interface:

IUserGrain user =
grainFactory.GetGrain<IUserGrain>("user-42");
IDiagnosticsExtension diagnostics =
user.AsReference<IDiagnosticsExtension>();
string status = await diagnostics.GetStatus();

The extension reference keeps the same grain identity. It doesn’t address a separate grain.

Incoming grain call filters run for extension calls. Filters should not assume every ImplementationMethod belongs to the grain implementation class.

Framework components can use IGrainContext component APIs and GetGrainExtension<T>() to provide an activation-specific extension. Those APIs are intended for runtime integrations that control grain activation setup. Application code should normally use AddGrainExtension and constructor-injected dependencies instead of mutating a grain context during activation.

Avoid exposing mutable grain state through a generic extension. Doing so bypasses the grain’s domain invariants and couples infrastructure to implementation details.