Nixie 1.1.6

dotnet add package Nixie --version 1.1.6
                    
NuGet\Install-Package Nixie -Version 1.1.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nixie" Version="1.1.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nixie" Version="1.1.6" />
                    
Directory.Packages.props
<PackageReference Include="Nixie" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nixie --version 1.1.6
                    
#r "nuget: Nixie, 1.1.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Nixie@1.1.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nixie&version=1.1.6
                    
Install as a Cake Addin
#tool nuget:?package=Nixie&version=1.1.6
                    
Install as a Cake Tool

Nixie

A lightweight, strongly typed actor model implementation for C#/.NET.

Run Tests NuGet Nuget

Overview

Nixie is a small actor framework built on the .NET Task Parallel Library. It focuses on compile-time type safety, nullable reference type support, simple actor lifecycle management, and fast in-process message passing.

Actors process messages asynchronously and encapsulate their own state. Callers communicate with actors through typed actor references, using either fire-and-forget Send or request/response Ask.

Features

  • Strongly typed actors: request and response types are expressed in actor interfaces and actor references.
  • Fire-and-forget actors: implement IActor<TRequest> and receive messages with Send.
  • Request/response actors: implement IActor<TRequest, TResponse> and receive messages with Ask or Send.
  • Struct message support: use IActorStruct<TRequest> and IActorStruct<TRequest, TResponse> with SpawnStruct to avoid reference-type messages.
  • Aggregate actors: use IActorAggregate<TRequest> or IActorAggregate<TRequest, TResponse> to process queued messages in batches.
  • Routers: round-robin and consistent-hash routers are available for class and struct actors.
  • Actor lookup: named actors can be resolved later with Get or GetStruct.
  • Actor shutdown: actors can be stopped by name or reference, immediately or through graceful shutdown.
  • Timers and scheduling: schedule one-time messages, periodic messages, actor shutdowns, and stop timers.
  • Sender context: actors can pass sender references and reply through the sender.
  • Actor context: actors receive access to Self, Sender, ActorSystem, Logger, and shutdown hooks.
  • Dependency injection: actors can be constructed through IServiceProvider, including additional spawn arguments.
  • Logging: pass an ILogger to ActorSystem for framework logging.
  • Wait support: ActorSystem.Wait() waits until current actor queues finish processing.
  • Nullable enabled: the library is built with nullable reference types enabled.

Requirements

  • .NET SDK 8.0 or later
  • C# nullable reference types are recommended

The package targets net8.0.

Installation

Using the .NET CLI:

dotnet add package Nixie --version 1.1.5

Using the NuGet Package Manager Console:

Install-Package Nixie -Version 1.1.5

Basic Usage

using Nixie;

public sealed class GreetMessage
{
    public string Greeting { get; }

    public GreetMessage(string greeting)
    {
        Greeting = greeting;
    }
}

public sealed class GreeterActor : IActor<GreetMessage>
{
    public Task Receive(GreetMessage message)
    {
        Console.WriteLine("Message: {0}", message.Greeting);
        return Task.CompletedTask;
    }
}

using ActorSystem system = new();

IActorRef<GreeterActor, GreetMessage> greeter =
    system.Spawn<GreeterActor, GreetMessage>();

greeter.Send(new GreetMessage("Hello, Nixie!"));

await system.Wait();

Actor Types

Fire-And-Forget Actors

Implement IActor<TRequest> when the actor processes messages without returning a response.

public sealed class CounterActor : IActor<string>
{
    private int count;

    public Task Receive(string message)
    {
        count++;
        return Task.CompletedTask;
    }
}

IActorRef<CounterActor, string> counter =
    system.Spawn<CounterActor, string>("counter");

counter.Send("increment");

Request/Response Actors

Implement IActor<TRequest, TResponse> when callers need a response.

public sealed class EchoActor : IActor<string, string>
{
    public Task<string?> Receive(string message)
    {
        return Task.FromResult<string?>(message);
    }
}

IActorRef<EchoActor, string, string> echo =
    system.Spawn<EchoActor, string, string>();

string? reply = await echo.Ask("hello", TimeSpan.FromSeconds(2));

Ask supports overloads with a timeout and with an explicit sender. When the timeout is reached, Nixie throws AskTimeoutException.

Struct Actors

Use struct actors for value-type request and response messages.

public readonly record struct Add(int Left, int Right);
public readonly record struct Sum(int Value);

public sealed class CalculatorActor : IActorStruct<Add, Sum>
{
    public Task<Sum> Receive(Add message)
    {
        return Task.FromResult(new Sum(message.Left + message.Right));
    }
}

IActorRefStruct<CalculatorActor, Add, Sum> calculator =
    system.SpawnStruct<CalculatorActor, Add, Sum>();

Sum sum = await calculator.Ask(new Add(2, 3));

Aggregate Actors

Aggregate actors receive batches instead of individual messages. They are useful when many queued messages can be processed more efficiently together.

public sealed class BatchActor : IActorAggregate<string>
{
    public Task Receive(List<string> messages)
    {
        Console.WriteLine("Batch size: {0}", messages.Count);
        return Task.CompletedTask;
    }
}

IActorRefAggregate<BatchActor, string> batch =
    system.SpawnAggregate<BatchActor, string>();

batch.Send("one");
batch.Send("two");
await system.Wait();

Aggregate request/response actors implement IActorAggregate<TRequest, TResponse> and receive List<ActorMessageReply<TRequest, TResponse>>.

Actor Context

Actors can ask for a typed context in their constructor. Nixie injects the context automatically.

public sealed class ParentActor : IActor<string>
{
    private readonly IActorContext<ParentActor, string> context;
    private readonly IActorRef<ChildActor, string> child;

    public ParentActor(IActorContext<ParentActor, string> context)
    {
        this.context = context;
        child = context.ActorSystem.Spawn<ChildActor, string>();
    }

    public Task Receive(string message)
    {
        child.Send(message, context.Self);
        return Task.CompletedTask;
    }
}

Context properties include:

  • ActorSystem: the owning actor system.
  • Self: a reference to the current actor.
  • Sender: the sender reference, when one was supplied.
  • Logger: the logger passed to ActorSystem, if any.
  • OnPostShutdown: event invoked when the actor is shut down.

Request/response contexts also expose Reply and ByPassReply for advanced response handling.

Spawning And Lookup

Actors can be unnamed or named. Named actors are unique per actor type within an actor system.

IActorRef<CounterActor, string> counter =
    system.Spawn<CounterActor, string>("counter");

IActorRef<CounterActor, string>? existing =
    system.Get<CounterActor, string>("counter");

Spawning a second actor with the same actor type and name throws NixieException.

You can pass additional constructor arguments after the optional name:

IActorRef<ConfiguredActor, string> actor =
    system.Spawn<ConfiguredActor, string>(null, 100, "mode-a");

Dependency Injection

Pass an IServiceProvider to ActorSystem to let Nixie resolve actor constructor dependencies.

using Microsoft.Extensions.DependencyInjection;
using Nixie;

IServiceCollection services = new ServiceCollection();
services.AddSingleton<IMyService, MyService>();

using ServiceProvider provider = services.BuildServiceProvider();
using ActorSystem system = new(provider);

IActorRef<MyActor, string> actor = system.Spawn<MyActor, string>();

DI can be combined with spawn arguments. Nixie provides the actor context, resolves registered services, and uses the extra arguments supplied to Spawn.

Routers

Router extension methods live in the Nixie.Routers namespace.

using Nixie.Routers;

IActorRef<RoundRobinActor<WorkerActor, WorkItem>, WorkItem> router =
    system.CreateRoundRobinRouter<WorkerActor, WorkItem>("workers", instances: 4);

router.Send(new WorkItem("job-1"));

Round-robin routers distribute messages across routees in order. You can create routees automatically by passing an instance count, or pass an existing list of actor references.

List<IActorRef<WorkerActor, WorkItem>> workers = new()
{
    system.Spawn<WorkerActor, WorkItem>(),
    system.Spawn<WorkerActor, WorkItem>()
};

IActorRef<RoundRobinActor<WorkerActor, WorkItem>, WorkItem> router =
    system.CreateRoundRobinRouter("workers", workers);

Consistent-hash routers require request messages to implement IConsistentHashable.

public sealed class WorkItem : IConsistentHashable
{
    public string Key { get; }

    public WorkItem(string key)
    {
        Key = key;
    }

    public int GetHash()
    {
        return Key.GetHashCode();
    }
}

IActorRef<ConsistentHashActor<WorkerActor, WorkItem>, WorkItem> router =
    system.CreateConsistentHashRouter<WorkerActor, WorkItem>("hashed-workers", 4);

Struct router variants are available through CreateRoundRobinRouterStruct and CreateConsistentHashRouterStruct.

Timers And Scheduling

Schedule a message once:

system.ScheduleOnce(counter, "increment", TimeSpan.FromSeconds(1));

Start and stop a periodic timer:

system.StartPeriodicTimer(
    counter,
    "counter-timer",
    "increment",
    initialDelay: TimeSpan.Zero,
    interval: TimeSpan.FromSeconds(1));

system.StopPeriodicTimer(counter, "counter-timer");

Stop all timers for an actor:

system.StopAllTimers(counter);

Schedule actor shutdown:

system.ScheduleShutdown(counter, TimeSpan.FromMinutes(5));

Struct actor timer variants are available through ScheduleOnceStruct and StartPeriodicTimerStruct.

Shutdown

Actors can be shut down by name or by reference.

bool stoppedByName = system.Shutdown<CounterActor, string>("counter");
bool stoppedByRef = system.Shutdown(counter);

Graceful shutdown waits until the actor confirms shutdown within the specified timeout.

bool stopped = await system.GracefulShutdown(counter, TimeSpan.FromSeconds(5));

Struct actors use ShutdownStruct and GracefulShutdownStruct.

Actors can subscribe to post-shutdown cleanup through context:

public sealed class CleanupActor : IActor<string>
{
    public CleanupActor(IActorContext<CleanupActor, string> context)
    {
        context.OnPostShutdown += () => Console.WriteLine("cleaned up");
    }

    public Task Receive(string message) => Task.CompletedTask;
}

Waiting For Work

ActorSystem.Wait() waits until currently known repositories have no pending messages and no actor is processing.

actor.Send("message");
await system.Wait();

This is useful in tests, command-line tools, and controlled shutdown flows.

Logging

Pass an ILogger to the actor system:

using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
});

using ActorSystem system = new(logger: loggerFactory.CreateLogger("Nixie"));

The logger is also available to actors through their context.

LazyTask

Nixie includes LazyTask<T> and LazyTaskMethodBuilder support for lazily started async methods.

private async LazyTask<MyResult> CreateResultAsync()
{
    await Task.Delay(100);
    return new MyResult();
}

LazyTask<MyResult> task = CreateResultAsync();
MyResult result = await task;

Development

Clone the repository and run tests:

dotnet test

The solution contains:

  • Nixie: the actor framework.
  • Nixie.Tests: xUnit tests covering actors, replies, routers, scheduling, DI, shutdown, logging, hashing, and LazyTask.

Contributing

Contributions are welcome. See CONTRIBUTING.md for guidelines.

License

Nixie is released under the MIT License. See LICENSE.

Name Origin

Nixies are shapeshifting water spirits in Germanic folklore.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Nixie:

Package Downloads
Kahuna.Core

.NET embeddable core for Kahuna: Distributed locks and reliable key-value store

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.6 73 5/29/2026
1.1.5 7,359 5/1/2025
1.1.4 1,319 4/20/2025
1.1.3 2,238 3/23/2025
1.1.2 676 3/23/2025
1.1.1 685 3/23/2025
1.1.0 1,254 3/3/2025
1.0.9 6,479 9/28/2024
1.0.8 4,977 8/3/2024
1.0.7 618 7/31/2024
1.0.6 663 7/30/2024
1.0.5 529 7/28/2024
1.0.4 522 7/28/2024
1.0.3 536 7/26/2024
1.0.2 530 7/26/2024
1.0.0 566 7/25/2024
0.0.8-alpha 505 12/13/2023
0.0.7-alpha 539 10/22/2023
0.0.6-alpha 218 10/18/2023
0.0.5-alpha 197 10/17/2023
Loading failed