抽取非基本验证到规则文件

抽取非基本验证到规则文件

基本验证业务验证,基本验证就是始终保持不变的验证规则,可以通过如下硬编码实现:

复制代码
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来负责)。所以要用规则文件来分开规则的编写,好处:

  1. 把修改的职责交给别人,比如项目经理、项目实施人员
  2. 代码不需要重新编译就能实现业务规则的修改

我们来解决下这个易变问题,假设有2公司:A和B。

A公司验证规则:

  1. 基本验证(就是Order类的验证规则的硬编码)
  2. 自定义验证规则:当前Order的下单网址必须来自于这几个url,如:www.cnblogs.com、www.cnblogs1.com、www.cnblogs2.com

B公司验证规则:

  1. 基本验证(同上) 
  2. 自定义验证规则:无

如果用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. 可以不写,但是最多只能存在1个这样的section
  2. 每行可以有逗号,也可以没有逗号
  3. 这个section的本意是:当前规则文件的变量全局定义、初始化工作
  4. 开头为declare,小写
  5. 结尾为end declare,小写

rule section:

  1. 必须存在1到多个
  2. 每行可以有逗号,也可以没有逗号
  3. 开头为rule "一些描述",小写
  4. 结尾为end rule,小写
  5. 如果存在3个rule时,最终会变成这样的js逻辑
  6. 复制代码
    加载underscore的js代码
    
    加载declare section的js代码
    
    if(rule1 conditions)
    {
          执行rule1的then语句
    }
    else if(rule2 conditions)
    {
          执行rule2的then语句
    }
    else if(rule3 conditions)
    {
          执行rule3的then语句
    }
    复制代码

大家可以下载代码进行自己修改,比如可以插入自己编写的js代码来更加简化或者更加贴近项目的自定义方法、函数。

原文地址:https://www.cnblogs.com/Leo_wl/p/3308897.html