A .NET Core 8 implementation of the GoRules Zen Engine - an Open-Source Business Rules Engine (BRE) that executes JSON Decision Models (JDM).
- Cross-platform: Runs on .NET Core 8
- JSON Decision Models: Load and execute JDM files
- Multiple Node Types: Support for Input, Output, Decision Table, Expression, and Switch nodes
- Flexible Loaders: File system, memory, and custom loaders
- Async/Await: Full async support throughout
- Tracing & Performance: Optional execution tracing and performance metrics
- Type Safe: Strongly typed with nullable reference types
dotnet add package ZenEngine.Core
using ZenEngine.Core;
using ZenEngine.Core.Models;
using System.Text.Json;
// Create a simple decision
var decisionJson = """
{
"id": "simple-decision",
"name": "Simple Decision",
"nodes": {
"input1": {
"id": "input1",
"name": "Input",
"type": "inputNode"
},
"expression1": {
"id": "expression1",
"name": "Transform",
"type": "expressionNode",
"content": {
"expressions": {
"result": "input * 2"
}
}
},
"output1": {
"id": "output1",
"name": "Output",
"type": "outputNode"
}
},
"edges": [
{
"id": "edge1",
"sourceId": "input1",
"targetId": "expression1"
},
{
"id": "edge2",
"sourceId": "expression1",
"targetId": "output1"
}
]
}
""";
// Execute the decision
var content = JsonSerializer.Deserialize<DecisionContent>(decisionJson)!;
var engine = DecisionEngine.Default;
var decision = engine.CreateDecision(content);
var result = await decision.EvaluateAsync(new { input = 15 });
Console.WriteLine(JsonSerializer.Serialize(result.Result));
// Output: {"result": 30}
Entry point for decision data.
Final result of the decision execution.
Spreadsheet-like rules evaluation with hit policies (first, collect).
Transform data using expressions with support for nested field assignment.
Conditional branching based on expression evaluation.
For direct decision creation without loading from external sources.
Load decisions from JSON files on disk.
var engine = new DecisionEngine(new FilesystemLoader("./decisions", keepInMemory: true));
var result = await engine.EvaluateAsync("pricing.json", context);
In-memory decision storage.
var loader = new MemoryLoader();
loader.Add("my-decision", decisionContent);
var engine = new DecisionEngine(loader);
Custom loading logic via delegates.
var loader = new ClosureLoader(async key => {
// Custom loading logic (database, API, etc.)
return await LoadFromDatabase(key);
});
The expression evaluator supports:
- Field access:
customer.age
,input.value
- Nested field assignment:
"nested.field": "expression"
- Literals: numbers, strings, booleans
- Simple comparisons and operations
ZenEngineException
: Base exception typeNodeExecutionException
: Node-specific execution errorsExpressionEvaluationException
: Expression parsing/evaluation errors
var options = new EvaluationOptions
{
IncludeTrace = true,
IncludePerformance = true,
MaxExecutionTimeMs = 5000
};
var result = await decision.EvaluateAsync(context, options);
// Access trace information
foreach (var trace in result.Trace)
{
Console.WriteLine($"Node {trace.Id}: {trace.ExecutionTime}ms");
}
// Performance metrics
var executionTime = result.Performance["executionTimeMs"];
# Clone the repository
git clone https://github.com/glglak/ZenEngine.NET.git
cd ZenEngine.NET
# Build the solution
dotnet build
# Run tests
dotnet test
# Run examples
cd examples/ZenEngine.Examples
dotnet run
This implementation aims for compatibility with the original Rust Zen Engine:
- Same JDM format
- Similar API patterns
- Compatible node types and behaviors
- Cross-language decision model sharing
For the official GoRules implementations, see:
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
MIT License - see the LICENSE file for details.
- GoRules for the original Zen Engine and JDM specification
- The .NET Community for excellent tooling and ecosystem