GoRules.Zen 0.5.0

dotnet add package GoRules.Zen --version 0.5.0
                    
NuGet\Install-Package GoRules.Zen -Version 0.5.0
                    
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="GoRules.Zen" Version="0.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GoRules.Zen" Version="0.5.0" />
                    
Directory.Packages.props
<PackageReference Include="GoRules.Zen" />
                    
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 GoRules.Zen --version 0.5.0
                    
#r "nuget: GoRules.Zen, 0.5.0"
                    
#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 GoRules.Zen@0.5.0
                    
#: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=GoRules.Zen&version=0.5.0
                    
Install as a Cake Addin
#tool nuget:?package=GoRules.Zen&version=0.5.0
                    
Install as a Cake Tool

GoRules.Zen

NuGet .NET

A cross-platform .NET binding for GoRules Zen - a high-performance business rules engine written in Rust.

🚀 安装

Step 1: 安装主包

dotnet add package GoRules.Zen

Step 2: 安装平台原生包

GoRules.Zen 需要对应平台的原生库才能运行。请根据你的目标平台选择:

平台 安装命令
Windows x64 dotnet add package GoRules.Zen.Native.win-x64
macOS x64 dotnet add package GoRules.Zen.Native.osx-x64
macOS ARM64 (Apple Silicon) dotnet add package GoRules.Zen.Native.osx-arm64
Linux x64 dotnet add package GoRules.Zen.Native.linux-x64
Linux ARM64 dotnet add package GoRules.Zen.Native.linux-arm64

例如,在 macOS Apple Silicon 上:

dotnet add package GoRules.Zen
dotnet add package GoRules.Zen.Native.osx-arm64

⚠️ 注意: 如果没有安装对应的原生包,运行时会收到清晰的错误提示,告知需要安装哪个包。

📋 支持的平台

平台 架构
Windows x64
macOS x64, ARM64 (Apple Silicon)
Linux x64, ARM64

📖 目录


快速开始

using GoRules.Zen;

// 1. 创建引擎(带文件加载器)
var options = new ZenEngineOptions
{
    Loader = path => File.ReadAllBytesAsync($"rules/{path}")
};

using var engine = new ZenEngine(options);

// 2. 执行决策
var result = await engine.Evaluate<MyResult>("my-rule.json", new { input = 42 });

// 3. 使用结果
Console.WriteLine($"输出: {result.Result}");

ZenEngine 使用

创建引擎

无 Loader 模式(需手动传入决策内容)
using var engine = new ZenEngine();

// 读取决策文件
var decisionContent = await File.ReadAllBytesAsync("my-rule.json");

// 创建决策对象
using var decision = engine.CreateDecision(decisionContent);

// 执行决策
var result = await decision.Evaluate<MyOutput>(new { input = 15 });
使用自定义 Loader

Loader 允许按需加载决策文件,支持从文件系统、数据库、远程 API 等任意来源获取:

// 从文件系统加载
var options = new ZenEngineOptions
{
    Loader = async path =>
    {
        var filePath = Path.Combine("Rules", path);
        return await File.ReadAllBytesAsync(filePath);
    }
};

using var engine = new ZenEngine(options);

// 直接通过 key 执行,引擎会自动调用 Loader
var result = await engine.Evaluate<MyOutput>("subfolder/my-rule.json", new { input = 10 });
// 从数据库加载
var options = new ZenEngineOptions
{
    Loader = async key =>
    {
        using var db = new MyDbContext();
        var rule = await db.Rules.FirstOrDefaultAsync(r => r.Key == key);
        return Encoding.UTF8.GetBytes(rule.JsonContent);
    }
};

使用自定义节点处理

CustomNodeHandler 允许你在决策流程中处理自定义节点类型:

var options = new ZenEngineOptions
{
    Loader = path => File.ReadAllBytesAsync($"rules/{path}"),
    
    CustomNodeHandler = async request =>
    {
        Console.WriteLine($"处理自定义节点: {request.Kind}");
        Console.WriteLine($"输入数据: {request.Input}");
        
        // 根据节点类型执行不同逻辑
        return request.Kind switch
        {
            "httpCall" => new CustomNodeResponse 
            { 
                Output = await CallExternalApi(request.Input) 
            },
            "dbQuery" => new CustomNodeResponse 
            { 
                Output = await QueryDatabase(request.Input) 
            },
            _ => new CustomNodeResponse 
            { 
                Output = new { error = "Unknown node type" } 
            }
        };
    }
};

using var engine = new ZenEngine(options);

执行决策

使用 Evaluate
// 基本执行
var result = await engine.Evaluate<MyOutput>("rule.json", new { input = 42 });
Console.WriteLine(result.Result);  // 获取结果

// 带 Trace 执行(用于调试)
var result = await engine.Evaluate<MyOutput>(
    "rule.json",
    new { input = 42 },
    new ZenEvaluationOptions { Trace = true }
);

if (result.Trace != null)
{
    Console.WriteLine($"执行跟踪: {result.Trace}");
}
使用 GetDecision 获取决策对象
// 获取可复用的决策对象
using var decision = await engine.GetDecision("rule.json");

// 多次执行同一决策
var result1 = await decision.Evaluate<MyOutput>(new { input = 10 });
var result2 = await decision.Evaluate<MyOutput>(new { input = 20 });
var result3 = await decision.Evaluate<MyOutput>(new { input = 30 });

安全执行(不抛异常)

使用 SafeEvaluateSafeGetDecision 返回 ZenSafeResult,不会抛出异常:

// 安全执行决策
var result = await engine.SafeEvaluate<MyOutput>("rule.json", new { input = 42 });

if (result.Success)
{
    Console.WriteLine($"成功: {result.Data?.Result}");
}
else
{
    Console.WriteLine($"失败: {result.Error}");
}

// 使用 Unwrap 方法
var data = result.UnwrapOr(new MyOutput { Value = "默认值" });               // 失败返回默认值
var dataOrDefault = result.UnwrapOrDefault();  // 失败返回 null
var dataOrThrow = result.Unwrap();             // 失败抛异常
// 安全获取决策
var decisionResult = await engine.SafeGetDecision("rule.json");

if (decisionResult.Success)
{
    using var decision = decisionResult.Data!;
    var evalResult = await decision.Evaluate<MyOutput>(new { input = 10 });
}

ZenDecision 使用

ZenDecision 是预加载的决策对象,适合多次执行同一决策:

using var engine = new ZenEngine();

// 从 JSON 内容创建决策
var jsonContent = await File.ReadAllBytesAsync("rule.json");
using var decision = engine.CreateDecision(jsonContent);

// 执行决策
var result = await decision.Evaluate<MyOutput>(new { input = 15 });

// 带选项执行
var resultWithTrace = await decision.Evaluate<MyOutput>(
    new { input = 15 },
    new ZenEvaluationOptions 
    { 
        Trace = true,
        MaxDepth = 10 
    }
);

ZenExpression 使用

ZenExpression 提供独立的表达式求值功能,无需创建引擎。

表达式求值

// 简单算术表达式
var sum = await ZenExpression.Evaluate<int>("1 + 2 + 3", null);
Console.WriteLine(sum);  // 6

// 带上下文的表达式
var result = await ZenExpression.Evaluate<int>(
    "price * quantity * (1 - discount)",
    new { price = 100, quantity = 5, discount = 0.1 }
);
Console.WriteLine(result);  // 450

// 字符串操作
var greeting = await ZenExpression.Evaluate<string>(
    "'Hello, ' + name + '!'",
    new { name = "World" }
);
Console.WriteLine(greeting);  // "Hello, World!"

// 数组操作
var filtered = await ZenExpression.Evaluate<int[]>(
    "filter(numbers, # > 10)",
    new { numbers = new[] { 5, 10, 15, 20, 25 } }
);
// 结果: [15, 20, 25]

一元表达式

一元表达式用于条件判断,返回布尔值:

// 比较表达式
var isGreater = await ZenExpression.EvaluateUnary(
    "> 100",
    new Dictionary<string, object> { { "$", 150 } }
);
Console.WriteLine(isGreater);  // true

// 包含判断
var isInList = await ZenExpression.EvaluateUnary(
    "'US', 'CN', 'JP'",
    new Dictionary<string, object> { { "$", "CN" } }
);
Console.WriteLine(isInList);  // true

// 范围判断
var inRange = await ZenExpression.EvaluateUnary(
    "[18..65]",
    new Dictionary<string, object> { { "$", 30 } }
);
Console.WriteLine(inRange);  // true

模板求值

模板使用 {{ }} 语法进行变量插值:

// 简单模板
var message = await ZenExpression.EvaluateTemplate(
    "Hello, {{ name }}!",
    new { name = "Alice" }
);
Console.WriteLine(message);  // "Hello, Alice!"

// 多变量模板
var email = await ZenExpression.EvaluateTemplate(
    "Dear {{ firstName }} {{ lastName }}, your order #{{ orderId }} has been shipped.",
    new { firstName = "John", lastName = "Doe", orderId = 12345 }
);

// 带表达式的模板
var invoice = await ZenExpression.EvaluateTemplate(
    "Total: ${{ price * quantity }}",
    new { price = 9.99, quantity = 3 }
);
Console.WriteLine(invoice);  // "Total: $29.97"

评估选项

ZenEvaluationOptions 用于控制决策执行行为:

var options = new ZenEvaluationOptions
{
    // 启用执行跟踪(用于调试)
    Trace = true,
    
    // 或使用 TraceMode 枚举
    TraceMode = TraceMode.Default,  // None 或 Default
    
    // 最大递归深度(防止无限循环)
    MaxDepth = 10
};

var result = await engine.Evaluate<MyOutput>("rule.json", context, options);

错误处理

使用异常处理

try
{
    var result = await engine.Evaluate<MyOutput>("rule.json", context);
}
catch (ZenException ex)
{
    Console.WriteLine($"Zen 错误: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"其他错误: {ex.Message}");
}

使用 SafeResult

var result = await engine.SafeEvaluate<MyOutput>("rule.json", context);

if (!result.Success)
{
    // 记录日志,返回默认值等
    logger.Error($"决策执行失败: {result.Error}");
    return defaultResponse;
}

return result.Data!.Result;

完整示例

ASP.NET Core 集成

// Program.cs
builder.Services.AddSingleton(sp =>
{
    var env = sp.GetRequiredService<IWebHostEnvironment>();
    
    return new ZenEngineOptions
    {
        Loader = async path =>
        {
            var rulesPath = Path.Combine(env.ContentRootPath, "Rules", path);
            return await File.ReadAllBytesAsync(rulesPath);
        }
    };
});

builder.Services.AddSingleton(sp =>
{
    var options = sp.GetRequiredService<ZenEngineOptions>();
    return new ZenEngine(options);
});

// Controller
[ApiController]
[Route("api/[controller]")]
public class PricingController : ControllerBase
{
    private readonly ZenEngine _engine;

    public PricingController(ZenEngine engine)
    {
        _engine = engine;
    }

    [HttpPost("calculate")]
    public async Task<IActionResult> Calculate([FromBody] PricingInput input)
    {
        var result = await _engine.SafeEvaluate<PricingOutput>(
            "pricing-rules.json",
            input
        );

        if (!result.Success)
        {
            return BadRequest(new { error = result.Error });
        }

        return Ok(result.Data?.Result);
    }
}

折扣计算示例

public class DiscountService
{
    private readonly ZenEngine _engine;

    public DiscountService()
    {
        _engine = new ZenEngine(new ZenEngineOptions
        {
            Loader = path => File.ReadAllBytesAsync(Path.Combine("Rules", path))
        });
    }

    public async Task<decimal> CalculateDiscount(Order order)
    {
        var context = new
        {
            totalAmount = order.TotalAmount,
            customerType = order.Customer.Type,
            itemCount = order.Items.Count,
            isHoliday = DateTime.Now.DayOfWeek == DayOfWeek.Saturday
        };

        var result = await _engine.Evaluate<DiscountResult>(
            "discount-rules.json",
            context
        );

        return result.Result.DiscountPercent;
    }
}

public record DiscountResult
{
    public decimal DiscountPercent { get; init; }
    public string? Reason { get; init; }
}

📚 更多资源

📄 许可证

MIT License - 详见 LICENSE

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 is compatible.  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 is compatible.  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.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.5.0 318 12/18/2025
0.4.0 291 12/17/2025
0.2.0 156 12/13/2025
0.1.0 155 12/13/2025