基本驗證與業務驗證,基本驗證就是始終保持不變的驗證規則,可以通過如下硬編碼實現:
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; } }
然後在用如下代碼validate, 把錯誤放到List中:
private bool ValidateBasicRule(Order order) { List<KeyValuePair<string, string>> errors = order.IsValid(); if (errors.Count > 0) { this.AddRange(errors); return false; } return true; } public static class DataAnnotationHelper { public static List<KeyValuePair<string, string>> IsValid<T>(this T o) { List<KeyValuePair<string, string>> errors = new List<KeyValuePair<string, string>>(); var descriptor = GetTypeDescriptor(typeof(T)); foreach (PropertyDescriptor propertyDescriptor in descriptor.GetProperties()) { foreach (var validationAttribute in propertyDescriptor.Attributes.OfType<ValidationAttribute>()) { if (!validationAttribute.IsValid(propertyDescriptor.GetValue(o))) { errors.Add(new KeyValuePair<string, string>(propertyDescriptor.Name, validationAttribute.FormatErrorMessage(propertyDescriptor.Name))); } } } return errors; } private static ICustomTypeDescriptor GetTypeDescriptor(Type type) { return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); } }
然後說說業務規則的易變
SaaS程序,或者業務規則極其易變時,就要采用其他方法來做了,不可能每個公司都用設計模式分開寫(雖然也行,但是不方便,公司業務規則多了後,對這些規則代碼的管理就是很高的成本,而且要developer來負責)。所以要用規則文件來分開規則的編寫,好處:
把修改的職責交給別人,比如項目經理、項目實施人員
代碼不需要重新編譯就能實現業務規則的修改
我們來解決下這個易變問題,假設有2公司:A和B。
A公司驗證規則:
基本驗證(就是Order類的驗證規則的硬編碼)
自定義驗證規則:當前Order的下單網址必須來自於這幾個url,如:www.cnblogs.com、www.cnblogs1.com、www.cnblogs2.com
B公司驗證規則:
基本驗證(同上)
自定義驗證規則:無
如果用A2D規則引擎來解決的話,需要怎麼做呢?
第一步當然是編寫規則文件,A公司的規則文件:
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
由於B公司沒有自定義規則,因此不需要編寫相應的規則文件
第二步,調整驗證邏輯,如下:
public class OrderService : BrokenRulesHolder { public int PlaceOrder(Order order) { this.ClearBrokenRules(); //進行基本規則驗證 if (!ValidateBasicRule(order)) return -1; //進行針對不同公司的規則驗證 if (!ValidateCompanyRule(order)) return -1; //其他操作,比如插入數據庫 return 100; } private bool ValidateCompanyRule(Order order) { BrokenRulesHolder tempBrokenRulesHolder = new BrokenRulesHolder(); string rulePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Rules", "OrderValidations", SessionContext.CompanyID + ".r"); using (RuleEngine engine = new RuleEngine(false)) //false代表:如果規則文件不存在不會報錯,而是忽略,默認為true { engine.BindRulePath(rulePath); //將規則文件的全路徑傳入引擎 engine.SetParameter("entity", order); engine.SetParameter("errors", tempBrokenRulesHolder); engine.Process(); } if (tempBrokenRulesHolder.BrokenRules.Count > 0) { this.AddRange(tempBrokenRulesHolder.BrokenRules); return false; } return true; } private bool ValidateBasicRule(Order order) { List<KeyValuePair<string, string>> errors = order.IsValid(); if (errors.Count > 0) { this.AddRange(errors); return false; } return true; } }
BrokenRule.cs代碼:
public class BrokenRulesHolder { private List<KeyValuePair<string, string>> brokenRules = new List<KeyValuePair<string, string>>(); public List<KeyValuePair<string, string>> BrokenRules { get { return this.brokenRules.AsReadOnly().ToList(); } } public void Add(string key, string msg) { brokenRules.Add(new KeyValuePair<string, string>(key, msg)); } public void AddRange(List<KeyValuePair<string, string>> rules) { brokenRules.AddRange(rules); } public void ClearBrokenRules() { brokenRules.Clear(); } }
demo代碼已經更新到A2D框架中了,這裡就不upload運行圖了。
A2D規則引擎已經內嵌了underscore 1.5.2,因此規則定義文件(.r文件)中可以寫相應的underscore函數來簡化寫法。
規則文件說明:
declare allowStores1=["www.cnblogs.com", "www.cnblogs1.com", "www.cnblogs2.com"] allowStores2=["www.cnblogs.com", "www.cnblogs1.com", "www.cnblogs2.com"] 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
declare section:
可以不寫,但是最多只能存在1個這樣的section
每行可以有逗號,也可以沒有逗號
這個section的本意是:當前規則文件的變量全局定義、初始化工作
開頭為declare,小寫
結尾為end declare,小寫
rule section:
必須存在1到多個
每行可以有逗號,也可以沒有逗號
開頭為rule "一些描述",小寫
結尾為end rule,小寫
如果存在3個rule時,最終會變成這樣的js邏輯
加載underscore的js代碼 加載declare section的js代碼 if(rule1 conditions) { 執行rule1的then語句 } else if(rule2 conditions) { 執行rule2的then語句 } else if(rule3 conditions) { 執行rule3的then語句 }
大家可以下載代碼進行自己修改,比如可以插入自己編寫的js代碼來更加簡化或者更加貼近項目的自定義方法、函數。
查看本欄目