伍佰目录 短网址
  当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

如何校验内存数据的一致性,DynamicExpresso 算是帮上大忙了

来源:本站原创 浏览:63次 时间:2022-12-30
一:背景1. 讲故事

记的在上一家公司做全内存项目的时候,因为一些关键表会在程序 startup 的时候全量灌入到内存中,但随着时间的推移,内存和数据库的同步偶尔会出现数据差异的情况,伴随着就是运营那边报过来的 bug,检查数据库的数据完整性很简单,直接写一些 sql 验证一下就好了,但校验内存中的数据就非常麻烦了,因为你不能像写 sql 一样直接去查生产中的内存集合,那怎么办呢?为了方便演示问题,先上一段演示代码:


   class Program
   {
       static void Main(string[] args)
       {
           var tradeList = new List<Trade>()
           {
               new Trade(){TradeID=1, TradeTitle="交易1", Created=Convert.ToDateTime("2020/8/1"), CustomerID=1},
               new Trade(){TradeID=2, TradeTitle="交易2", Created=Convert.ToDateTime("2020/8/5"),CustomerID=2},
               new Trade(){TradeID=3, TradeTitle="交易3", Created=Convert.ToDateTime("2020/8/10"), CustomerID=3}
           };
       }
   }

   class Trade
   {
       public int TradeID { get; set; }

       public string TradeTitle { get; set; }

       public DateTime Created { get; set; }

       public int CustomerID { get; set; }
   }

上面的 tradeList 就是内存中的集合,现在有一个问题,我想查询一下 trade 表中 CustomerID in (1,2,10) && Created <= '2020-08-01' 的记录是否和内存中的 tradelist 一致。

用 sql 验证太简单了,直接在查询分析器里面写一下sql 搞定,如下图:

那在 UI 上 怎么验证呢?

二:寻找解决方法1. 在UI上自定义高级查询

这个也是大家最容易想到的,使用多个 if 叠加查询条件,如下代码所示:


       static void Main(string[] args)
       {
           var tradeList = new List<Trade>()
           {
               new Trade(){TradeID=1, TradeTitle="交易1", Created=Convert.ToDateTime("2020/8/1"), CustomerID=1},
               new Trade(){TradeID=2, TradeTitle="交易2", Created=Convert.ToDateTime("2020/8/5"),CustomerID=2},
               new Trade(){TradeID=3, TradeTitle="交易3", Created=Convert.ToDateTime("2020/8/10"), CustomerID=3}
           };

           IEnumerable<Trade> query = tradeList;

           //UI
           var queryCustomerIDList = new List<int>() { 1, 2, 10};
           var queryCreated = "2020-08-01";

           if (queryCustomerIDList.Count > 0)
           {
               query = query.Where(m => queryCustomerIDList.Contains(m.CustomerID));
           }
           if (string.IsNullOrEmpty(queryCreated))
           {
               query = query.Where(m => m.Created <= Convert.ToDateTime(queryCreated));
           }

           //最后的结果
           var list = query.ToList();
       }

问题貌似是可以解决,但是这种用 if 叠加的方式不觉得太不灵活了吗?如果客户心情不好,又来了一个 TradeID between 1 and 10 的筛选条件,那上面的代码是不是还得加一个 TradeID 的判断 ?太麻烦了,还得继续寻找更灵活的姿势。

2. 使用DataTable

哈哈,大家看到 DataTable 是不是有一点懵逼,可不要小瞧这玩意,人家可是直接支持 sql 查询的哦,这灵活性不容小觑哈,上一段代码说话:


       static void Main(string[] args)
       {
           var tradeList = new List<Trade>()
           {
               new Trade(){TradeID=1, TradeTitle="交易1", Created=Convert.ToDateTime("2020/8/1"), CustomerID=1},
               new Trade(){TradeID=2, TradeTitle="交易2", Created=Convert.ToDateTime("2020/8/5"),CustomerID=1},
               new Trade(){TradeID=3, TradeTitle="交易3", Created=Convert.ToDateTime("2020/8/10"), CustomerID=3}
           };

           var table = CopyToDataTable(tradeList);

           var query = table.Select("CustomerID in (1,2,10) and Created <= '2020-08-01' and TradeID >= 1 and TradeID <= 10")
                           .Select(m => new Trade()
                           {
                               TradeID = Convert.ToInt32(m[0]),
                               TradeTitle = Convert.ToString(m[1]),
                               Created = Convert.ToDateTime(m[2]),
                               CustomerID = Convert.ToInt32(3)
                           }).ToList();
       }

       public static DataTable CopyToDataTable<T>(IEnumerable<T> array)
       {
           var ret = new DataTable();
           foreach (PropertyDescriptor dp in TypeDescriptor.GetProperties(typeof(T)))
               ret.Columns.Add(dp.Name);
           foreach (T item in array)
           {
               var Row = ret.NewRow();
               foreach (PropertyDescriptor dp in TypeDescriptor.GetProperties(typeof(T)))
                   Row[dp.Name] = dp.GetValue(item);
               ret.Rows.Add(Row);
           }
           return ret;
       }

是不是很强大,直接将文本化的 sql 塞入到 DataTable 中,你想什么样的查询你就写什么样的 sql 就 ok 啦,当然,理论归理论,在我的场景中肯定是不会这么玩的,毕竟内存中的 trade 有上千万行,转成 DataTable 不是给自己挖坑嘛,那有没有其他的方式呢?

3. 使用 表达式树 (ExpressionTree)

我想很多人看到 表达式树 都会退避三舍,虽然这玩意很强大,但是太复杂了,它会将你的查询语句拆解成树中的节点从而构建一棵非常复杂的树结构,其实 DataTable 对 sql语句的解析也是在内存中构建了一棵解析树,所以这玩意太反人类了,比如你要构建 i > 5 的查询,你需要下面这样的硬编码,这还是非常简单的哈,复杂的会让你吐血。

            ParameterExpression param = Expression.Parameter(typeof(int), "i");
           ConstantExpression constExp = Expression.Constant(5, typeof(int));
           BinaryExpression greaterThan = Expression.GreaterThan(param, constExp);
           Expression<Func<int, bool>> f = Expression.Lambda<Func<int, bool>>(greaterThan, param);
           Func<int, bool> mydelegate = f.Compile();
           Console.WriteLine(mydelegate(5));

从图中可以看到,5>5 = False 是没有问题的,既然表达式树是可以解决类似这样的场景,聪明的你应该会想到,开源社区是否又类似封装好的 ExpressionTree 开发包呢?说实话,还真有。。。

4. DynamicExpresso 开发工具包

开源大法好,github地址:https://github.com/davideicardi/DynamicExpresso , 这玩意实现了 将文本化的 C# 语句 动态转换成 delegate,这句话是什么意思呢?大家可以看一下这张图:

从上图可以看到,你可以 写一些文本化的 C# 语句,然后经过 DynamicExpresso 处理后转换成了可执行 delegate,如果你没看懂,我用代码表示一下,如下图:

其中: 30 = 5 * 8 / 2 + 10 ,重点在于这里的 数学表达式 是文本的,有了这个思路,那我是不是也可以将 tradeList 的查询条件文本化表示,如下代码:


           var interpreter = new Interpreter();

           interpreter.Reference(typeof(System.Linq.Enumerable));

           interpreter.SetVariable("arr", new int[] { 1, 2, 10 });

           string whereExpression = "(trade.CustomerID == 1 || trade.CustomerID==2 || trade.CustomerID==10) && " +
                                    "trade.Created <= Convert.ToDateTime(\"2020-08-01\") &&" +
                                    "trade.TradeID >= 1 && " +
                                    "trade.TradeID <=10";

           Func<Trade, bool> queryFunc = interpreter.ParseAsDelegate<Func<Trade, bool>>(whereExpression, "trade");

           var list = tradeList.Where(queryFunc).ToList();

           var i = Enumerable.Contains(new int[] { 1, 2, 3 }, 3);

问题搞定,还是比较完美的

三:总结

总的来说,有了DynamicExpresso ,我就可以将 文本化的 C#语句 直接丢给 Where 条件就可以灵活检索,完美的解决了在内存中查询 tradelist 数据分布情况,当然目前的 DynamicExpresso 还有很多语句不支持,不过都在完善中,期待大家支持点赞加贡献。


  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net