RulesEngine 動態規則引擎指南
用途:以 JSON 定義業務規則,執行期動態評估,不需重新編譯部署
NuGet:
RulesEngine授權:MIT
支援:.NET 6+、.NET Framework 4.6.2+
GitHub:microsoft/RulesEngine
什麼是 RulesEngine
RulesEngine 是 Microsoft 開源的輕量級規則引擎。核心概念很簡單:把業務判斷邏輯從程式碼中抽出來,用 JSON 格式定義規則,在執行期動態載入和評估。
傳統做法是把判斷條件寫死在程式碼裡:
// ❌ 每次改條件都要改程式碼 → 重新編譯 → 重新部署
if (temperature > 80 && duration > 30)
{
await RaiseAlarmAsync(AlarmSeverity.Critical, "溫度過高");
}
else if (temperature > 60 && humidity > 90)
{
await RaiseAlarmAsync(AlarmSeverity.Warning, "高溫高濕");
}
RulesEngine 的做法是把這些條件放進 JSON:
[{
"WorkflowName": "AlarmRules",
"Rules": [
{
"RuleName": "HighTemperature",
"Expression": "Temperature > 80 AND Duration > 30",
"SuccessEvent": "Critical",
"ErrorMessage": "溫度 $(Temperature)°C 超過閾值"
},
{
"RuleName": "HighTempHighHumidity",
"Expression": "Temperature > 60 AND Humidity > 90",
"SuccessEvent": "Warning",
"ErrorMessage": "高溫高濕警告"
}
]
}]
規則存在資料庫或設定檔中,修改條件不需要改程式碼。現場工程師可以透過 UI 調整閾值,立即生效。
什麼場景該用 vs 不該用
✅ 適合使用 RulesEngine 的場景
| 場景 | 原因 |
|---|---|
| 告警規則動態設定 | 不同設備、不同客戶的告警閾值不同,現場需要自行調整 |
| 多租戶不同邏輯 | 同一套系統,不同客戶 MES 的判斷規則不同 |
| 非開發人員需要修改規則 | 現場工程師改告警條件,不應該需要工程師改程式碼 |
| 規則頻繁變更 | 每週甚至每天都在調整的判斷條件 |
| 資料品質檢查 | 感測器數據的合理性驗證,規則隨設備校正調整 |
❌ 不適合使用 RulesEngine 的場景
| 場景 | 更好的選擇 |
|---|---|
| 固定不變的業務邏輯 | 直接寫 C#,清楚且效能好 |
| 簡單的 if-else(< 5 條) | 程式碼更直覺,不需要引入框架 |
| 型別安全很重要 | RulesEngine 用字串表達式,沒有編譯期檢查 |
| 已有參數化的條件系統 | 例如 EndConditionType enum + 參數的設計已經夠用 |
| 效能敏感的熱路徑 | 表達式解析有開銷,每秒上萬次評估時考慮其他方案 |
判斷原則
問自己:「這個規則會不會需要在不部署程式碼的情況下修改?」 如果答案是「是」,就適合用 RulesEngine。如果規則穩定不變,直接寫 C# 更好。
核心概念
| 概念 | 說明 |
|---|---|
| Workflow | 一組相關規則的容器,每個 Workflow 有自己的名稱 |
| Rule | 單一規則,包含名稱、表達式、成功/失敗事件 |
| Expression | C# Lambda 風格的表達式字串(由 System.Linq.Expressions 編譯) |
| RuleParameter | 傳入規則的資料物件(用 input1、input2 在表達式中存取) |
| GlobalParams | Workflow 層級的共用計算變數 |
| LocalParams | Rule 層級的區域計算變數 |
| Actions | 規則通過/失敗時觸發的動作(內建 OutputExpression 或自訂 Action) |
表達式語法
RulesEngine 的表達式基於 C# Lambda 語法,但用字串形式寫入 JSON:
// 比較運算
"Temperature > 80"
"Status == \"Running\""
"ErrorCount >= 3"
// 邏輯運算
"Temperature > 80 AND Pressure < 50"
"IsConnected == false OR ErrorCount > 10"
// 方法呼叫
"DeviceName.Contains(\"CMP\")"
"Tags.Any(t => t == \"Critical\")"
// 數學運算
"(Temperature - BaselineTemp) / BaselineTemp * 100 > 10"
評估流程
呼叫 ExecuteAllRulesAsync(workflowName, input) 後,RulesEngine 會依序走過以下步驟。每條 Rule 都會獨立評估一次,結果彙總為 RuleResultTree 清單:
流程重點:
- Rules 會全部跑完(非短路):
ExecuteAllRulesAsync不會在第一條成功時停止;若只想取第一條通過的規則,需在呼叫端自行.FirstOrDefault(r => r.IsSuccess) - OnSuccess / OnFailure 可選:不寫 Action 時,規則只回報
IsSuccess結果;需要執行副作用(log、raise alarm)才寫 Action - LocalParams 與 GlobalParams:可在 Expression 中引用,前者作用範圍限於該 Rule,後者整個 Workflow 共用
- 例外處理:Expression 編譯錯誤、型別不符等會落在
RuleResultTree.ExceptionMessage,不會中斷其他 Rule
規則結構
以 classDiagram 呈現 Workflow / Rule / Action 的組合關係:
JSON 與 C# 物件的對應:Workflow 對應最外層陣列元素,Rules 內的每個物件對應一個 Rule;執行結果會被包成 RuleResultTree(支援巢狀規則時的樹狀結果)。
安裝
dotnet add package RulesEngine
基本範例
using RulesEngine.Models;
using System.Text.Json;
// 1. 定義規則(通常從 DB 或檔案載入)
var workflowJson = @"[{
""WorkflowName"": ""TemperatureCheck"",
""Rules"": [
{
""RuleName"": ""CriticalTemperature"",
""Expression"": ""Temperature > 80"",
""SuccessEvent"": ""Critical""
},
{
""RuleName"": ""WarningTemperature"",
""Expression"": ""Temperature > 60 AND Temperature <= 80"",
""SuccessEvent"": ""Warning""
},
{
""RuleName"": ""NormalTemperature"",
""Expression"": ""Temperature <= 60"",
""SuccessEvent"": ""Normal""
}
]
}]";
var workflows = JsonSerializer.Deserialize<Workflow[]>(workflowJson)!;
// 2. 建立引擎
var rulesEngine = new RulesEngine.RulesEngine(workflows);
// 3. 準備輸入資料
var sensorData = new {
Temperature = 85.5,
Pressure = 101.3,
DeviceId = "CMP-01"
};
// 4. 執行規則
var results = await rulesEngine.ExecuteAllRulesAsync("TemperatureCheck", sensorData);
foreach (var result in results)
{
if (result.IsSuccess)
{
Console.WriteLine($"規則 {result.Rule.RuleName} 通過 → 事件: {result.Rule.SuccessEvent}");
// 輸出:規則 CriticalTemperature 通過 → 事件: Critical
}
}
程式碼量比較:RulesEngine vs 硬寫
當規則數量增長時,差異會越來越明顯:
// ❌ 硬寫:每加一條規則就要改程式碼,改完要部署
public AlarmSeverity EvaluateAlarm(SensorData data)
{
if (data.Temperature > 80 && data.Duration > 30) return AlarmSeverity.Critical;
if (data.Temperature > 60 && data.Humidity > 90) return AlarmSeverity.Warning;
if (data.Pressure < 50 || data.Pressure > 150) return AlarmSeverity.Warning;
if (data.FlowRate == 0 && data.PumpRunning) return AlarmSeverity.Critical;
// ... 20 條規則後,這個方法變成噩夢
return AlarmSeverity.Normal;
}
// ✅ RulesEngine:規則在 JSON/DB 中,程式碼永遠是同一段
public async Task<IEnumerable<AlarmSeverity>> EvaluateAlarmAsync(SensorData data)
{
var results = await _rulesEngine.ExecuteAllRulesAsync("AlarmRules", data);
return results
.Where(r => r.IsSuccess)
.Select(r => Enum.Parse<AlarmSeverity>(r.Rule.SuccessEvent));
}
延伸閱讀
- 公司場景 Pattern — 告警規則、MES 整合、Recipe 條件、DI 整合
- FluentValidation 驗證 — 靜態驗證規則(編譯期定義的驗證)