![]() |
VOOZH | about |
dotnet add package Nixie --version 1.1.7
NuGet\Install-Package Nixie -Version 1.1.7
<PackageReference Include="Nixie" Version="1.1.7" />
<PackageVersion Include="Nixie" Version="1.1.7" />Directory.Packages.props
<PackageReference Include="Nixie" />Project file
paket add Nixie --version 1.1.7
#r "nuget: Nixie, 1.1.7"
#:package Nixie@1.1.7
#addin nuget:?package=Nixie&version=1.1.7Install as a Cake Addin
#tool nuget:?package=Nixie&version=1.1.7Install as a Cake Tool
A lightweight, strongly typed actor model implementation for C#/.NET.
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.
IActor<TRequest> and receive messages with Send.IActor<TRequest, TResponse> and receive messages with Ask or Send.IActorStruct<TRequest> and IActorStruct<TRequest, TResponse> with SpawnStruct to avoid reference-type messages.IActorAggregate<TRequest> or IActorAggregate<TRequest, TResponse> to process queued messages in batches.Get or GetStruct.Self, Sender, ActorSystem, Logger, and shutdown hooks.IServiceProvider, including additional spawn arguments.ILogger to ActorSystem for framework logging.ActorSystem.Wait() waits until current actor queues finish processing.The package targets net8.0.
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
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();
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");
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.
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 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>>.
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.
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");
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.
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.
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.
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;
}
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.
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.
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;
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.Contributions are welcome. See for guidelines.
Nixie is released under the MIT License. See .
Nixies are shapeshifting water spirits in Germanic folklore.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
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 |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.1.7 | 610 | 6/2/2026 |
| 1.1.6 | 254 | 5/29/2026 |
| 1.1.5 | 8,042 | 5/1/2025 |
| 1.1.4 | 1,513 | 4/20/2025 |
| 1.1.3 | 2,431 | 3/23/2025 |
| 1.1.2 | 871 | 3/23/2025 |
| 1.1.1 | 877 | 3/23/2025 |
| 1.1.0 | 1,445 | 3/3/2025 |
| 1.0.9 | 6,678 | 9/28/2024 |
| 1.0.8 | 5,169 | 8/3/2024 |
| 1.0.7 | 809 | 7/31/2024 |
| 1.0.6 | 854 | 7/30/2024 |
| 1.0.5 | 720 | 7/28/2024 |
| 1.0.4 | 714 | 7/28/2024 |
| 1.0.3 | 726 | 7/26/2024 |
| 1.0.2 | 721 | 7/26/2024 |
| 1.0.0 | 758 | 7/25/2024 |
| 0.0.8-alpha | 505 | 12/13/2023 |
| 0.0.7-alpha | 541 | 10/22/2023 |
| 0.0.6-alpha | 220 | 10/18/2023 |