基本驗證與業務驗證,基本驗證就是始終保持不變的驗證規則,可以通過如下硬編碼實現:
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代碼來更加簡化或者更加貼近項目的自定義方法、函數。
查看本欄目