一:背景

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;//UIvar 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 还有很多语句不支持,不过都在完善中,期待大家支持点赞加贡献。

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

  1. 高德地图路线规划 时间_路线准、播报拥堵及时,这次自驾出行高德地图可算是帮了大忙...

    在我们日常生活中,自驾已经成为一种很普遍的出行方式,不仅在时间上灵活,特别是一家人出行也比较方便.伴随自驾出行的除了爱车外,一款靠谱的地图导航软件也成为了必不可少的旅行伙伴. 目前比较常用的地图导航软 ...

  2. 利用percona-toolkit工具检查MySQL数据库主从复制数据的一致性,以及修复。

    利用percona-toolkit工具检查MySQL数据库主从复制数据的一致性,以及修复. 一.pt-table-checksum检查主从库数据的一致性 pt-table-checksum在MASTE ...

  3. 谈了千百遍的缓存数据的一致性问题

    " 灵魂拷问 保证缓存和数据库的一致性很简单吗? 有哪些方式能保证缓存和数据库的一致性呢? 如果发生了缓存和数据库数据不一致的情况怎么办呢? 在上篇文章我们介绍了缓存的定义分类以及优缺点等, ...

  4. HDFS的特性以及如何保证数据的一致性

    链接:https://www.nowcoder.com/questionTerminal/962225fa78e74ba7b1d7d7792407acc6?orderByHotValue=1& ...

  5. oracle数据块一致性检查的4种方法

    什么是数据块一致性? 每一个数据块头部都有一个"校验和"字段 当数据块被写回磁盘前,Oracle会重新计算这个校验和 并记录到这个字段,最终写回磁盘 下次数据块被读入内存时,Ora ...

  6. 关于 RMAN 备份 数据块 一致性的讨论

    今天和 杭州恒生 的一个朋友讨论一个RMAN 在备份时数据块一致性的问题. 关于RMAN 的备份原理参考blog: RMAN 系列(一)---- RMAN 体系结构概述 http://blog.csd ...

  7. Mysql 扩展性设计之数据切分、那么数据切分后会带来哪些问题呢?比如分布式事务、数据的一致性、垂直切分和水平切分应用场景

    Mysql 扩展性设计之数据切分.那么数据切分后会带来哪些问题呢?比如分布式事务.数据的一致性.垂直切分和水平切分应用场景 前言.什么是数据切分 垂直(纵向)切分.水平(横向)切分.他们各自的特点 垂 ...

  8. MySQL和InnoDB体系结构,内存数据对象,基本建表操作

    本节内容主要关于mysql体系结构和InnoDB存储引擎的体系架构(包括后台线程和内存结构).InnoDB关键特性.checkpoint机制,以及刷脏磁盘的不同时机和方式.最后是在mysql客户端通过 ...

  9. hazelcast配置内存_在内存数据网格中引入hazelcast imdg

    hazelcast配置内存 Today's article will be oriented to a very specific concept, which is the In-Memory Da ...

最新文章

  1. PHP的SQL注入攻击的技术实现以及预防措施
  2. linux下安装ftp服务器
  3. Freemarker中如何遍历List
  4. scala akka通信机制
  5. golang 字符串操作实例
  6. 单编译framework相关模块
  7. 编程不是一种知识,而是一门手艺。
  8. 数电实验Verilog-数字钟
  9. DDR SDRAM内存测试的一种官方方案(简单易懂)
  10. scanf输入回车问题
  11. android layout.inflater,LayoutInflater.inflate详解
  12. Continuous Integration 对 ABAP 技术栈来说意味着什么
  13. 新建STM32F427IIH标准库工程
  14. 生成服务器证书 启用HTTPS 生成自签名证书
  15. cad旋转命令_如何将CAD图形旋转至水平位置?
  16. 第十二届蓝桥杯省赛C/C++B组2021
  17. Python爬虫实战之哔哩哔哩二维码登录申请
  18. 一加3T viper4android,哈曼卡顿Cortana智能音箱外观/内部规格全曝光
  19. Nutanix 在 2020 .NEXT 大会推出 Kubernetes 平台即服务产品
  20. 将阿拉伯数字转成中文字

热门文章

  1. npm 安装 chromedriver 失败的解决办法
  2. GitHub项目管理维护实用教程
  3. WebRTC 音频模块单独编译 --【转载】
  4. nodejs中文件,目录的操作(1)
  5. 软件工程之个人项目--词频统计
  6. 充分利用Microsoft Planner的6种方法
  7. 文件下载至storage_如何防止Storage Sense在Windows 10上删除下载的文件
  8. Vue使用Vuex一步步封装并使用store
  9. 计算机网络udp实验时间戳请求报文与应答报文的表格填写,自考计算机网络管理历年(2007.1-2013.1)试题及答案(标有页码)...
  10. 中兴智能视觉大数据:人脸识别技术目前处于“用的不够,用的不好”