Lightweight, developer-friendly rule engine for .NET
Add the NuGet package (after publish, or use a project reference while developing):
dotnet add package RuleFlow
RuleFlow targets .NET 10 (net10.0).
This matches the Basic Rules scenario in the playground (BasicRulesScenario.cs).
using RuleFlow.Core.Engine;
using RuleFlow.Core.Rules;
var order = new Order { Amount = 1500 };
var rules = RuleSet.For<Order>("ApprovalRules")
.Add(Rule.For<Order>("High amount")
.When(o => o.Amount > 1000)
.Then(o => o.RequiresApproval = true)
.Because("Amount exceeds threshold"));
var engine = new RuleEngine();
var result = engine.Evaluate(order, rules);
// View execution results
Console.WriteLine(result.Explain()); // formatted text via IRuleResultFormatter
Console.WriteLine(result.ToDebugString()); // human-readable tree with status markers
Console.WriteLine(result.ToDebugJson()); // structured JSON for UIs and tooling
// result properties: Executions (flat), Root (tree), AppliedRules (matched names), Metrics (if observability enabled)
Register IRuleEngine and related services with AddRuleFlow(). See ASP.NET Core integration.
Enable observability to track metrics and monitor rule execution:
var options = new RuleExecutionOptions<Order>
{
EnableObservability = true,
EnableDetailedTiming = true
};
var result = engine.Evaluate(order, rules, options);
if (result.Metrics != null)
{
Console.WriteLine($"Rules matched: {result.Metrics.RulesMatched}");
Console.WriteLine($"Duration: {result.Metrics.TotalElapsedMilliseconds}ms");
}
Observability has zero overhead when disabled and supports custom observers for logging, monitoring, and analytics. See Observability for details.
From the repository root:
dotnet run --project samples/RuleFlow.ConsoleSample
The interactive menu runs scenarios that correspond to the documentation pages. When you change behavior or APIs, update both the relevant scenario and the matching doc page.
For rules whose logic is authored as data (JSON/UI/database), use structured ConditionNode trees and IConditionEvaluator<T> instead of C# lambdas in When. Same engine and explainability; mapping is described in Persistence and Dynamic conditions.
ThenIf, dynamic ConditionNode