Expression经验之二:LambdaExpression变换

想了好久标题的名称,姑且就叫做LambdaExpression变换吧。那到底要变换什么呢?说的简单些就是要把表达式

Expression<Func<Student, bool>> filter=s=>s.Name.Contains("a") && s.Age>=20;

这样的表达试转换成

Expression<Func<DataRow, bool>> filter = r=>((string)r["Name"]).Contains("a") && ((int)r["Age"])>=20;

也许你会问,干嘛要这样做呢?举个例子,

说DAL里有一个类StudentProvider用于对student进行数据库的增删改查的操作。我们就拿查询来说,查询可以有很多的条件。以往可能会有类似的方法:

public IEnumerable<Student> GetStudentsByName(string name);
public Student GetStudentById(int id);

但是别忘了今天的世界有了Expression,我们应该向这些落后的(别打我,窃以为的)方法说再见了。高颜值的接口当然要写成这样了:

public IEnumerable<Student> GetStudents(Expression<Func<Student, bool>> filter);

于是我们来看看这个方法的实现,

     public IEnumerable<Student> GetStudents(Expression<Func<Student, bool>> filter)
        {
            using (var connection=new SqlConnection("some connection string"))
            {
                var selectSql = "SELECT * FROM Student";
                using (var adapter = new SqlDataAdapter(selectSql, connection))
                {
                    var ds = new DataSet();
                    adapter.Fill(ds, "table");
                    return from raw in ds.Tables["table"].AsEnumerable() select new Student(raw);
                }
            }
        } 

实现用到了Linq to DataSet, 其实我们真正想做的是

return from raw in ds.Tables["table"].AsEnumerable().Where(filter) select new Student(raw)

但是问题是Where只接受

Func<DataRow, bool> predicate

到这里,终于明白了为什么要做LambdaExpression变换了吧。

前一篇中我们看到了ExpressionVisitor的强大,这里我们还要用他来解决问题。我们引入一个ConvertMemberToColumnVisitor:

     public class ConvertMemberToColumnVisitor : ExpressionVisitor
        {
            private readonly Expression _columnOwnerExpression;
            private readonly string _memberOwnerName;

            public ConvertMemberToColumnVisitor(Expression columnOwnerExpression, string memberOwnerName)
            {
                _columnOwnerExpression = columnOwnerExpression;
                _memberOwnerName = memberOwnerName;
            }

            protected override Expression VisitMember(MemberExpression node)
            {
                var parameterExpression = node.Expression as ParameterExpression;
                if (parameterExpression != null && parameterExpression.Name == _memberOwnerName)
                {
                    return Expression.Convert(Expression.Call(_columnOwnerExpression, typeof(DataRow).GetMethod("get_Item", new []{typeof(string)}), Expression.Constant(node.Member.Name)),
                        ((PropertyInfo)node.Member).PropertyType);
                }

                return base.VisitMember(node);
            }
        }

很简单,很定一个我们要替代成的表达式,当然我们还是用parameter name来匹配所以要给定一个参数名。

有了这个Visitor后,一切问题都简单了:

     public IEnumerable<Student> GetStudents(Expression<Func<Student, bool>> filter)
        {
            using (var connection=new SqlConnection("some connection string"))
            {
                var selectSql = "SELECT * FROM Student";
                using (var adapter = new SqlDataAdapter(selectSql, connection))
                {
                    var ds = new DataSet();
                    adapter.Fill(ds, "table");

                    var p1 = Expression.Parameter(typeof(DataRow), "r");
                    var converter = new ConvertMemberToColumnVisitor(p1, filter.Parameters[0].Name);
                    var newExp = converter.Visit(filter);
                    var lambda = Expression.Lambda<Func<DataRow, bool>>(((LambdaExpression)newExp).Body, p1);
                    var predicate = lambda.Compile();
                    return from raw in ds.Tables["table"].AsEnumerable().Where(predicate) select new Student(raw);
                }
            }
        } 

当然lambda.Compile()会消耗性能,这个我们后面再想办法缓存它。

时间: 2024-08-11 08:40:41

Expression经验之二:LambdaExpression变换的相关文章

Expression经验之前言

对于C#中的Expression特性想必从事C#开发的同学都不会陌生,网上和园子里都有很多的好的文章介绍.我想也没有必要再去写一些文章去介绍,科普或是从入门到精通之类的. 这系列的随笔主要是就实现工作和学习过程中的一些思考.问题的解决以及自觉有趣的发现等作些分享. 借助于Expression, 我们可以写些很优雅(至少个人觉得)的代码. 例于开发WPF的同学们每天要接触的ViewModel中, OnPropertyChanged(() => Name); 要比 OnPropertyChanged

脱壳经验(二)--如何优化

PE脱壳后,文件长度一般会增大,原因有以下几点: 1.脱壳后,壳的尸体依然存在:2.脱壳后,重新建立了新的Import节.Reloc节等:3.有些加壳程序,将原程序的某些节跟外壳增加的某些节合并后,搬移到了其他位置.4.脱壳时候,因为采用的是dump内存,所以,在内存中对齐的PE镜象一般也会大于原始的PE文件,各个节的dump实际尺寸也会较原始PE文件的实际尺寸大. 解决方案: (1) 脱壳后,将各个有效的节分别转存到临时文件中.如:将“.text节,单独转存到一个text_tem.bin中”,

工作碰上的技术问题及处理经验(二)

续上一篇随笔: https://www.cnblogs.com/kingstarer/p/8469016.html <工作碰上的技术问题及处理经验> 由于内容有很多空格,如果直接在正文粘贴,发表后空格会消失,导致版本看起来比较难看. 所以我把主要内容做为代码发表. 我觉得每天把工作碰上的问题做一个简单的笔记挺不错的,一来可以锻炼自己的表达能力,二来也方便自己以后复查,因为以后工作很可能再碰上同样的问题. 由于我每次记录笔记时,可能只是记录了关键字,而要发出来做为共享随笔,只有这些关键字肯定是不

C#心得与经验(二)

本周学到很多C#关于Interface, Array的知识,在这里简单复习一下几个易混的地方,重在理解. 一.Interface 使用as来避免多态时没有接口的Exception: Document [] folder = new Document[5]; for (int i = 0; i < 5; i++) { if (i % 2 == 0) { folder[i] = new BigDocument("Big Document # " + i); } else { fold

Quartz2D 编程指南(二)变换、图案、阴影

概览 图形上下文 路径 颜色与颜色空间 变换 图案 阴影 渐变 透明层 Quartz 2D 中的数据管理 位图与图像遮罩 CoreGraphics 绘制 Layer 5.变换 简介 Quartz 2D 绘制模型定义了两种独立的坐标空间:用户空间(用于表现文档页)和设备空间(用于表现设备的本地分辨率). 当我们需要一个点或者显示文档时, Quartz 会将用户空间坐标系统映射到设备空间坐标系统.当前映射关系的变换矩阵称为 CTM(current transformation matrix). Qu

OpenGL超级宝典笔记二 - 基础变换

1.向量: 点乘:float m3dDotProduce3(u,v):返回两个单位向量的余弦值 叉乘:float m3dCrossProduct3(result,u,v):返回垂直于两个向量定义的平面的向量 2.矩阵: OpenGL使用的是列优先排序的矩阵 单位矩阵(对角线为1,其他为0):任何向量乘以一个单位矩阵都不会发生任何改变 3.变换:最终获得的变换矩阵会应用到每个顶点 视图矩阵x模型矩阵x投影矩阵->投影摄像机的位置变换,物体对象的位置变换,投影裁剪变换 若顶点向量为Vert,则变换公

手机测试总结----经验总结二

测试的时候对于一个产品应该怎么样测试.总结四点(正确.错误.极端.变态) 测试之前,尽可能获取被测产品信息(通过大量沟通来实现). 白盒:(语句覆盖.判定覆盖.条件覆盖.排列覆盖) //一般开发做单元测试. 等价类划分:输入数据两类:有效.无效. 边界值:取值范围:(1~10).在压线的情况下,程序的使用. 因果图:收索界面:输入条件.通过不同的条件,不同的结果.(不同的条件组合,是否实现不同结果的实践) 正交实践设计方法:大的模块.分小模块.分页面.框体.界面有多少元素.(由烦化简) 功能图方

暑期报修项目经验分享二(附原码)

楔子 第二期项目经验分享,其实整个项目流程,我们只是参与了其中一小部分,仅仅是添加了材料管理模块的内容,在第一期的实践里面,主要维护材料类别的增查改操作.二期维护做的是材料出入库处理,因为我处理的是入库管理这一块,所以分享下做这一块的经验. 需求分析: 在分析业务需求的时候,因为考虑到出入库在一张数据表里面,即是共用数据,那么type区分出入库,在写代码的时候一个反人类的操作就是把流水号设置成整形,虽然影响不大,另外关于材料类别代码这里,因为材料类别是三级联动设置,所以我们只取最后一级的代码,因

[转]UiPath实践经验总结(二)

本文转自:https://www.cnblogs.com/ybyebo/p/10086473.html 1.       UI操作容易受到各种意外的干扰,因此应该缩短UI操作阶段的总体时间.而为了缩短UI操作阶段的总体时间,应该将UI操作尽量放在一起,将后台的各种操作尽量放在UI操作的前后.例如,现在有一个Assign和两个Click需要执行,那么比较推荐的设计是Assign->Click->Click或者Click->Click->Assign,而不是Click->Assi