.NET深入解析LINQ框架(三:LINQ优雅的前奏)

简介: 阅读目录: 1.动态LINQ查询(动态构建Expression表达式树) 2.DLR动态语言运行时(基于CLR之上的动态语言运行时) 1】.动态LINQ查询(动态构建Expression表达式树) 什么是动态LINQ查询?LINQ的编写是静态的,因为C#是基于静态类型系统原理设计的,在编写时已经确定类型,也就是在编译时就已经知道将要执行什么样的查询,条件是什么、排序方式是什么等等。

阅读目录:

  • 1.动态LINQ查询(动态构建Expression<T>表达式树)
  • 2.DLR动态语言运行时(基于CLR之上的动态语言运行时)

1】.动态LINQ查询(动态构建Expression<T>表达式树)

什么是动态LINQ查询?LINQ的编写是静态的,因为C#是基于静态类型系统原理设计的,在编写时已经确定类型,也就是在编译时就已经知道将要执行什么样的查询,条件是什么、排序方式是什么等等。那么很大一部分应用场合中我们需要根据用户的选择来查询数据源,以往我们都是通过判断的方式来拼接查询的SQL字符串,但是现在我们面对是强类型的LINQ查询,是否可以很方便的进行类似查询。其实也没有什么好神秘的,基本的实现原理是通过动态的构建表达式树来实现IQueryable<T>接口的查询。

其实动态LINQ查询所能执行的最关键的因素在于Expression<T>对象是可以被动态编译成可以执行的委托对象,委托对象是完全可以被直接使用的可执行代码段,这就为动态LINQ查询提供了基础。对于IEnumerable<T>类型的查询表达式方法都知道它的执行是不会直接接受Expression<T>类型对象的,那么动态LINQ是否能工作于IEnumerable<T>接口?其实可以的,有个很隐蔽的窍门隐藏在IQueryable<T>扩展方法对象Queryable中,也就是AsQueryable<T>方法,它返回的是一个实现了IQueryable<T>接口的EnumerableQuery对象,该对象的实现内容不是很复杂,将动态拼接的数据结构Expression<T>对象编译成可以执行的匿名函数,然后直接执行查询。

我们来看一下EnumerableQuery对象的重点,它肯定有一个地方是将Expression<T>对象Compiler的地方。

View Code
 1 private IEnumerator<T> GetEnumerator()
 2         {
 3             if (this.enumerable == null)
 4             {
 5                 EnumerableRewriter rewriter = new EnumerableRewriter();
 6                 Expression<Func<IEnumerable<T>>> expression2 =
 7                     Expression.Lambda<Func<IEnumerable<T>>>(rewriter.Visit(this.expression), (IEnumerable<ParameterExpression>)null);
 8                 this.enumerable = expression2.Compile()(); //(1)重点      
 9             }
10             return this.enumerable.GetEnumerator();
11         }

在上述代码中的“(1)重点”的地方,我们很清楚的看见表达式树被动态编译后然后紧接着又被执行,这里就能看出为什么IEnumerable<T>对象需要能够被转换成IQueryable<T>对象。这样就可以消除IEnumerable<T>、IQueryable<T>这两个接口之间的动态查询瓶颈。

为什么需要动态LINQ查询,上面说过问题出在我们没办法在运行时再去编写Lambda表达式了,都知道Lambda表达式到最后就是被编译成Expression表达式树对象,所以我们可以在运行时自己动态的构建Expression对象,这样就可以将动态构建出来的表达式树对象直接传入到需要的方法中。如果查询的数据对象是IEnumerable<T>则会被动态编译成可以执行的委托然后直接执行,如果查询的是IQueryable<T>则顺其自然的被提供程序解析执行。

下面我们来看一个简单的动态查询例子:

View Code
1  //构造Student数组
2             Student[] StudentArrary = new Student[3]
3             {
4                 new Student(){Name="王清培", Age=24, Sex="", Address="江苏南京"},
5                 new Student(){Name="陈玉和", Age=23, Sex="", Address="江苏盐城"},
6                 new Student(){Name="金源", Age=22, Sex="", Address="江苏淮安"}
7             };

这是一组数据,为了简单测试就不搞那么麻烦的Linq to Sql数据源了。我们将要通过动态的构建表达式树来做为查询的逻辑,以往我们的Lambda在这个时候派不上用场了,在运行时我们无法再去构建委托类型。

现在的需求是从界面上接受一个Name值的输入,LINQ的查询只需要直接写就行了。

View Code
1 var list = from i in StudentArrary where i.Name == "王清培" select i; 

但是我们需要动态的构建表达式树来执行查询,表达式树的任何一个节点都有相对应的Expression派生类型,所以我们只要将相关类型组装起来就行了。由于我建的示例程序的类型是控制台程序,所以我们就用简短的方式演示一下如何构建表达式树。

View Code
 1 ParameterExpression parameter = Expression.Parameter(typeof(Student), "stu");//表示二元运算符的左边参数名称
 2             //表示"stu"参数的"stu.Name"中的Name属性,Name属性必须是反射获取的元数据才行,这样框架就才可以找到它
 3             MemberExpression property = Expression.MakeMemberAccess(parameter, typeof(Student).GetMember("Name")[0]);
 4             //表示常量值
 5             Console.WriteLine("请输入要查询人的名称:");
 6             ConstantExpression name = Expression.Constant(Console.ReadLine());//从用户输入流中读取值
 7             BinaryExpression binary = Expression.MakeBinary(ExpressionType.Equal, property, name);//拼接==运算符的左边、右边
 8             //完整的表达式是Lambda才对
 9             LambdaExpression lambda = Expression.Lambda(binary, parameter);
10             //重要的就在这里,我们将完整的Lambda表达式直接变成可以执行的委托
11             Func<Student, bool> wheredelegate = lambda.Compile() as Func<Student, bool>;
12             //将编译后的可执行委托直接放入Where方法中执行
13             var list2 = StudentArrary.AsQueryable<Student>().Where(wheredelegate);
14             foreach (var i in list2)
15             {
16                 Console.WriteLine("查询列表:");
17                 Console.WriteLine("姓名:{0},年龄:{1},地址:{2}", i.Name, i.Age, i.Address);
18             }
19             Console.ReadLine();

图例:

该例子的重点是如何动态构建逻辑,根据不同的项目要求完全可以将类似的功能封装起来供以后重复使用。如果觉得手动编写表达式树很麻烦的话,建议可以找一个辅助类能将Lambda表达式的对象树都能打印出来的工具,然后对着这棵树在去写就简单多了。

关于动态LINQ的第三方的API不是很多,比较常用的就是Dynamic.cs的使用,具体我没有用过,看过相关文档应该还是比较方便的。它的内部原理其实还是动态的构建表达式树,只不过这部分工作被人家做了,而我们使用起来却简单的很多。

2】.DLR动态语言运行时(基于CLR之上的动态语言运行时)

从C#1一路走来,它变的越来越强大,.NET平台变得无所不能。很多人还一直咬着.NET不能跨平台,不能支持动态对象,不支持非托管等等理由来排斥它,然而他们所不知的是.NET已经悄无声息的做出来一大举动,那就是在静态语言运行时上嵌入动态语言运行时环境。我想不是微软不能支持所谓的缺点,而是它确实有它的本意。

动态语言运行时是在.NET4.0中引入的建立在CLR之上的运行时环境,目的是为了在静态语言中能够借鉴动态语言运行时的优点,比如强大的类型随意变换,这点在设计应用开发框架时尤其重要,任何一个好的特性都需要大面积的使用模式才能变的更完美。

说到动态运行时就不得不提JS中让人兴奋的var定义的对象特性了,如果没有留意在设计框架时而存在的烦恼其实很难发现动态运行和静态语言之间的好与不好。很明显的例子就是当我们定义一个数据类型的对象时,无法再在后期运行时对它进行其他类型的使用,看一个简单的例子:

View Code
1 dynamic obj = 1;//整形  
2 obj = "1";//字符串 
3 obj = new { Name = "王清培", Age = 24, Address = "江苏" };//匿名对象类型 

在运行时我们可以随意的设计对象的类型,我大胆的假设完全可以用动态运行时特性设计类似人工智能系统,提供基本原型,然后根据用户自己的思维方式构建任意对象树。技术科研是很不错的方向,企业应用可能还有待商讨。

以往我们很难在运行时为对象动态的添加属性、行为、事件,通过动态语言运行时我们可以很自如的添加想要的东西。

下面我们来看一个简单的例子,在运行时动态的构建一个对象类型,在以前我们只有用动态编译、CodeDom技术来实现,这里将变的很简单。

View Code
 1 static void Main(string[] args)
 2         {
 3             dynamic objModel = new ExpandoObject();//初始化可以动态添加属性、方法、事件的ExpandoObject对象
 4             objModel.Name = "王清培";//设置属性值
 5             objModel.Age = 24;
 6             objModel.WriteEvent = null;//存放事件的委托字段定义
 7             objModel.WriteEvent += new Action<string>(WriteName);//设置事件的方法
 8             objModel.WriteEvent(objModel.Name + objModel.Age);
 9             Console.ReadLine();
10         }
11         public static void WriteName(string info)
12         {
13             Console.WriteLine(info);
14         }

一个很简单的例子告诉我们可以在C#中去编写如JS中的动态对象功能,不过目前还不是很成熟,动态对象的成员没有智能提示,应该是还没有被大面积使用起来,以后肯定也是一大美餐;

总结:LINQ框架的基本使用原理就全部结束了,后面我们就来学习如何能让LINQ查询我们自定义的数据源。很多朋友都喜欢自己写ORM框架,那么你肯定少不了对LINQ的支持吧?后面我们就来详细的讲解如何扩展IQueryable<T>、IQueryableProvider<T>两个重量级接口,只有他们两个才能让我们和LINQ对话,这两个接口还是很神秘的。

作者:王清培

出处:http://www.cnblogs.com/wangiqngpei557/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

目录
相关文章
|
11天前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
6天前
|
SQL API 数据库
Python中的SQLAlchemy框架:深度解析与实战应用
【4月更文挑战第13天】在Python的众多ORM(对象关系映射)框架中,SQLAlchemy以其功能强大、灵活性和易扩展性脱颖而出,成为许多开发者首选的数据库操作工具。本文将深入探讨SQLAlchemy的核心概念、功能特点以及实战应用,帮助读者更好地理解和使用这一框架。
|
29天前
|
开发框架 网络协议 .NET
深入.net框架
深入.net框架
11 0
|
1月前
|
物联网 调度 开发者
构建高效Python Web应用:异步编程与Tornado框架解析
【2月更文挑战第27天】 在处理高并发的Web应用场景时,传统的同步阻塞模型往往难以满足性能需求。本文将深入探讨Python世界中的异步编程概念,并结合Tornado这一轻量级、非阻塞式Web服务器及框架,展示如何构建高性能的Web应用。通过实例驱动的方法论,我们将剖析Tornado的核心组件,包括其IOLoop、异步HTTP客户端和服务器端处理机制,以及与协程集成的细节。文章旨在为开发者提供一套实践指南,帮助他们利用Python实现快速响应和资源高效的Web服务。
28 2
|
1月前
|
算法 Java API
探索Java并发编程:Fork/Join框架的深度解析
【2月更文挑战第26天】随着多核处理器的普及,并发编程在软件开发中的重要性日益凸显。Java语言提供了多种并发工具,其中Fork/Join框架是处理分而治之问题的一个强大工具。本文将深入探讨Fork/Join框架的设计原理、使用场景及与传统线程池的区别,并通过实例演示如何有效利用该框架提升程序性能。
|
1月前
|
SQL API 数据处理
新一代实时数据集成框架 Flink CDC 3.0 —— 核心技术架构解析
本文整理自阿里云开源大数据平台吕宴全关于新一代实时数据集成框架 Flink CDC 3.0 的核心技术架构解析。
688 0
新一代实时数据集成框架 Flink CDC 3.0 —— 核心技术架构解析
|
1月前
|
监控 调度 开发者
构建高效Python Web应用:异步编程与Tornado框架深度解析
【2月更文挑战第20天】在处理高并发的Web应用时,传统的同步阻塞模型往往难以满足性能要求。本文将深入探讨Python异步编程的原理及其优势,并通过Tornado框架的案例分析,展示如何构建一个高效的异步Web服务。我们将从异步IO的基础讲起,逐步过渡到Tornado的核心组件,最终实现一个能够承载大量并发连接的Web服务器,为追求高性能Web解决方案的开发者提供实践指南。
|
3月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
38 0
|
30天前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
28 0
|
30天前
mvc.net分页查询案例——mvc-paper.css
mvc.net分页查询案例——mvc-paper.css
5 0

推荐镜像

更多