.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/

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

目录
相关文章
|
10月前
|
监控 Cloud Native 测试技术
.NET技术深度解析:现代企业级开发指南
每日激励:“不要一直责怪过去的自己,他曾经站在雾里也很迷茫”。我是蒋星熠Jaxonic,一名在代码宇宙中探索的极客旅人。从.NET Framework到.NET 8,我深耕跨平台、高性能、云原生开发,践行领域驱动设计与微服务架构,用代码书写技术诗篇。分享架构演进、性能优化与AI融合前沿,助力开发者在二进制星河中逐光前行。关注我,共探技术无限可能!
.NET技术深度解析:现代企业级开发指南
|
人工智能 API 开发者
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
1018 27
|
开发框架 .NET 中间件
.net8 使用 license 证书授权案例解析
本文介绍了如何使用 `.NET CLI` 创建并改造一个 `ASP.NET Core Web API` 项目,以实现基于许可证的授权机制。具体步骤包括创建项目、添加必要的 NuGet 包(如 `Standard.Licensing` 和 `Swashbuckle.AspNetCore`),以及修改 `Program.cs` 文件以集成自定义的许可证验证中间件。项目结构中新增了 `LicenseController` 接口用于处理授权相关操作,并通过测试流程验证了默认天气接口在未授权和授权状态下的响应情况。整个过程确保了应用程序能够在启动时正确验证许可证,保障系统的安全性与可控性。
830 10
.net8 使用 license 证书授权案例解析
|
人工智能 自然语言处理 搜索推荐
ViDoRAG:开源多模态文档检索框架,多智能体推理+图文理解精准解析文档
ViDoRAG 是阿里巴巴通义实验室联合中国科学技术大学和上海交通大学推出的视觉文档检索增强生成框架,基于多智能体协作和动态迭代推理,显著提升复杂视觉文档的检索和生成效率。
1169 8
ViDoRAG:开源多模态文档检索框架,多智能体推理+图文理解精准解析文档
|
机器学习/深度学习 人工智能 Java
Java机器学习实战:基于DJL框架的手写数字识别全解析
在人工智能蓬勃发展的今天,Python凭借丰富的生态库(如TensorFlow、PyTorch)成为AI开发的首选语言。但Java作为企业级应用的基石,其在生产环境部署、性能优化和工程化方面的优势不容忽视。DJL(Deep Java Library)的出现完美填补了Java在深度学习领域的空白,它提供了一套统一的API,允许开发者无缝对接主流深度学习框架,将AI模型高效部署到Java生态中。本文将通过手写数字识别的完整流程,深入解析DJL框架的核心机制与应用实践。
1015 3
|
C# Android开发 iOS开发
2025年全面的.NET跨平台应用框架推荐
2025年全面的.NET跨平台应用框架推荐
874 23
|
监控 前端开发 API
一款基于 .NET MVC 框架开发、功能全面的MES系统
一款基于 .NET MVC 框架开发、功能全面的MES系统
813 5
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
958 0
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
496 7

推荐镜像

更多
  • DNS