zoukankan      html  css  js  c++  java
  • 抽取非基本验证到规则文件

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

    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代码来更加简化或者更加贴近项目的自定义方法、函数。

  • 相关阅读:
    2019年4月
    20190423
    20190419
    20190418
    20190417
    free命令详解(转载)
    https改造过程中的一个坑
    GitLab 实现代码自动部署(转载自https://segmentfault.com/a/1190000011561808)
    js和php写日历
    shell递归遍历目录的方法
  • 原文地址:https://www.cnblogs.com/aarond/p/A2D-RuleEngine.html
Copyright © 2011-2022 走看看