Skip to content

Repository files navigation

nodora-dotnet

.NET bindings for the Nodora rule engine.

Install

dotnet add package Nodora

The package targets netstandard2.0, so it works on .NET Framework 4.6.1+, .NET Core, and .NET 5/6/7/8+.

Prebuilt runtimes

The native engine ships inside the package under runtimes/<rid>/native and is loaded automatically at run time. Prebuilt for:

RID
linux-x64 ✅
linux-arm64 ✅
osx-x64 ✅
osx-arm64 ✅
win-x64 ✅

Other platforms build from source (needs Go 1.24+ with cgo) — see Development.

Usage

using NodoraEngine;

var ruleset = Nodora.Compile(@"
    rule AdultCheck {
        out is_adult = input.age >= 18
    }
");

using var evaluator = ruleset.CreateEvaluator();
var result = evaluator.Evaluate("AdultCheck", new { age = 21 });

Console.WriteLine(result.Outputs["is_adult"].GetBoolean()); // True

Evaluate takes any object; it is serialized to JSON with System.Text.Json and becomes the rule's input. Outputs are returned as JsonElement values you read with the usual accessors (GetBoolean(), GetInt32(), GetString(), …).

Signals emitted by a rule are returned in the result. You can read the raw list directly:

foreach (var signal in result.EmittedSignals)
{
    Console.WriteLine($"{signal.Name}({string.Join(", ", signal.Args)})");
}

Or subscribe listeners to specific signals with a SignalDispatcher, so you don't have to switch over each signal by name:

var signals = new SignalDispatcher();
signals.On("Greet", s => Console.WriteLine($"Greetings, {s.Arg<string>(0)}!"));
signals.OnAny(s => logger.Debug($"signal {s.Name}"));  // catch-all

var result = evaluator.Evaluate("AdultCheck", new { age = 21, name = "Alice" });
signals.Dispatch(result);

On and OnAny return an IDisposable you can dispose to unsubscribe. Arg<T>(index) deserializes a positional argument to the type you expect.

Dispatch is synchronous: the decision (rule outputs) is already returned before you dispatch, so keep handlers fast. For a slow or best-effort effect such as sending an email, have the handler hand the work off to a queue or outbox and return, rather than blocking in the handler.

A precompiled ruleset (e.g. the output of nodora compile) can be loaded without recompiling the source:

var ruleset = Nodora.FromJson(rulesetJson);

The evaluator owns a native handle — dispose it (a using statement is the easiest way) when you are done. A single evaluator can be reused across many inputs.

API

Member Purpose
Nodora.Compile(source) Compile Nodora source to a ruleset.
Nodora.FromJson(json) Load a precompiled ruleset.
Ruleset.ToJson() Serialize the ruleset back to JSON.
Ruleset.CreateEvaluator() Build a reusable evaluator.
Evaluator.Evaluate(rule, input) Run a rule against any serializable input.
Evaluator.EvaluateJson(rule, json) Run a rule against a raw JSON input.
EvaluationResult { Outputs, EmittedSignals } Named outputs and emitted signals.
SignalDispatcher Route emitted signals to named listeners.
NodoraException Thrown on compile/evaluation errors.

Development

The managed library builds without the native engine, but running tests or the example requires it. Building the engine needs a Go 1.24+ toolchain with cgo (a working C compiler):

make native    # build the native engine into runtimes/<host-rid>/native
make test      # build the engine, then run the test suite
make example   # run the example

On Windows, make native runs build/build-native.ps1; you can also run that script directly. It requires a MinGW-w64 gcc on PATH for cgo.

To produce the NuGet package locally (with whatever runtimes you have built):

make pack      # -> dist/Nodora.<version>.nupkg

CI (.github/workflows/build.yml) builds the native engine for every supported RID and packs them all into a single cross-platform package on tag pushes.

License

Apache-2.0.

About

.NET bindings for the Nodora rule engine

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages