資料驗證分為基本驗證 (固定規則:必填、長度、範圍) 與業務驗證 (頻變規則:各客戶不同業務邏輯)。A2D 規則引擎將業務規則外部化為 .r 規則檔案,無需重新編譯即可熱更新,降低維護成本並授權非開發人員維護規則。
public class Order
{
[Required]
[Range(typeof(decimal), "1", "10000")]
public decimal Price { get; set; }
[Required]
[StringLength(30)]
public string Customer { get; set; }
[Required(AllowEmptyStrings = true)]
[StringLength(50)]
public string StoreID { get; set; }
}
private bool ValidateBasicRule(Order order)
{
var errors = order.IsValid();
if (errors.Count > 0) { this.AddRange(errors); return false; }
return true;
}
public static class DataAnnotationHelper
{
public static List> IsValid(this T o)
{
var errors = new List>();
var descriptor = GetTypeDescriptor(typeof(T));
foreach (PropertyDescriptor prop in descriptor.GetProperties())
{
foreach (var attr in prop.Attributes.OfType())
{
if (!attr.IsValid(prop.GetValue(o)))
{
errors.Add(new KeyValuePair(prop.Name,
attr.FormatErrorMessage(prop.Name)));
}
}
}
return errors;
}
}
www.cnblogs.com 等)declare allowStores=["www.cnblogs.com", "www.cnblogs1.com", "www.cnblogs2.com"]
end
declare rule "test"
when !_.contains(allowStores, entity.StoreID)
then errors.Add("StoreID", "StoreID value not right")
end rule
引擎設定 ignoreMissing=true,檔案不存在時自動略過。
public class OrderService : BrokenRulesHolder
{
public int PlaceOrder(Order order)
{
this.ClearBrokenRules();
// 1. 基本驗證
if (!ValidateBasicRule(order)) return -1;
// 2. 公司業務規則
if (!ValidateCompanyRule(order)) return -1;
// 3. 業務邏輯 (如寫入資料庫)
return 100;
}
private bool ValidateCompanyRule(Order order)
{
var tempHolder = new BrokenRulesHolder();
string rulePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Rules", "OrderValidations", SessionContext.CompanyID + ".r");
using (RuleEngine engine = new RuleEngine(false)) // false = 檔案不存在不報錯
{
engine.BindRulePath(rulePath);
engine.SetParameter("entity", order);
engine.SetParameter("errors", tempHolder);
engine.Process();
}
if (tempHolder.BrokenRules.Count > 0)
{
this.AddRange(tempHolder.BrokenRules);
return false;
}
return true;
}
}
public class BrokenRulesHolder
{
private List> brokenRules = new List>();
public List> BrokenRules
{ get { return brokenRules.AsReadOnly().ToList(); } }
public void Add(string key, string msg) { brokenRules.Add(new KeyValuePair(key, msg)); }
public void AddRange(List> rules) { brokenRules.AddRange(rules); }
public void ClearBrokenRules() { brokenRules.Clear(); }
}
declare
allowStores1=["a", "b"],
allowStores2=["c", "d"]
end declare
rule "rule 1"
when !allowStores1.contains(entity.StoreID)
then errors.Add("StoreID", "StoreID value not right")
end rule
rule "rule 2"
when !allowStores2.contains(entity.StoreID)
then errors.Add("StoreID", "StoreID value not right")
end rule
// 載入 underscore.js
// 載入 declare 區段轉換的 JS 代碼
if (rule1_condition) { 執行 rule1 then 區塊 }
else if (rule2_condition) { 執行 rule2 then 區塊 }
else if (rule3_condition) { 執行 rule3 then 區塊 }
| 傳統硬編碼 | A2D 規則引擎 |
|---|---|
| 修改需重新編譯部署 | 規則檔案熱更新,無需重啟 |
| 僅開發人員可修改 | 專案經理/實施顧問可維護 |
| 規則分散在程式碼中 | 集中管理、版本控制、審計追蹤 |
| 多客戶需分支程式碼 | 同一代碼基底,差異化規則檔案 |
程式碼已整合至 A2D Framework:sourceforge.net/projects/a2dframework/
查看本欄目