.NET深入解析LINQ框架(六:LINQ执行表达式)

简介:

在看本篇文章之前我假设您已经具备我之前分析的一些原理知识,因为这章所要讲的内容是建立在之前的一系列知识点之上的,为了保证您的阅读顺利建议您先阅读本人的LINQ系列文章的前几篇或者您已经具备比较深入的LINQ原理知识体系,防止耽误您的宝贵时间。

到目前为止我们对LINQ的执行原理已经很清楚了,从它的前期构想到它真正为我们所用都有足够的证据,但是似乎问题并没有我们想的那么简单,问题总是在我们使用中频频出现尤其是新技术的使用,当然有问题才能有进步。[王清培版权所有,转载请给出署名]

一:LINQ执行表达式

在研究LINQ的过程中,参考了很多技术文章还有技术书籍,毫无疑问的是Linq to Provider的调用入口都是将Lambda表达式解析成Expression<T>表达式对象,跟Linq to Object不同,Linq to Object是将Lambda直接解析成泛型Func类型的委托,但是我们很多人包括我自己都忽视了一个很大的细节,就是Provider在内部将对Expression<T>进行执行,并非我们所理解的那样将表达式Expression<T>对象完全解析成等价的SQL,也就是说Expression<T>并不是我们说看到的那样单纯,它具有双重上下文逻辑在里面。

我们都是直接使用LINQ作为查询接口,VS在最后编译的时候负责对LINQ的语法进行解析并且翻译成对应的扩展方法调用。我们忽视一个重要的环节,就是VS对LINQ进行解析翻译的时候是会执行LINQ表达式的,这点非常重要。之前我一直以为VS只负责将LINQ的表达式翻译成等价的扩展方法调用,后来发现VS为了满足我们在前期无法确定对象条件的情况下进行Where字句的拼接,允许我们在编写LINQ语句的时候带有逻辑判断表达式在里面,这个功能对我们进行多条件组合查询时相当方便,不需要在进行IF、ELSE的多个判断,只需要顺其自然的在LINQ中的第一个表达式中进行判断就行了。追求优雅代码的同志很不希望在一个既有LINQ查询又带有链式查询的方法中用两种查询方式,如果LINQ能满足大部分的查询功能那最完美;[王清培版权所有,转载请给出署名]

为了说明LINQ在编译时会被VS执行,我们用LINQPad工具看一下便知;

LINQ查询表达式:from truck in TB_CX_TRUCKs where 1==1 select truck

LINQ等价的链式方法: TB_CX_TRUCKs.Where (truck => True)

图1:

1

如果没有执行按道理是直接解析成Lambda的格式(truck)=>1==1才对,然后让LINQ to Provider提供程序负责处理才对,也许觉得没有实质的意思反正是恒等的表达式所以解析成这样。我们在换一种写法看看;[王清培版权所有,转载请给出署名]

LINQ查询表达式:from truck in TB_CX_TRUCKs where string.IsNullOrEmpty("1111") select truck

LINQ等价的链式方法:TB_CX_TRUCKs.Where (truck => String.IsNullOrEmpty ("1111"))

图2:

2

由此可以得出一个结论,LINQ语句是会被执行和解析的两个动作,在还没有进入到提供程序时已经可以看出LINQ是可以附带一些执行逻辑在里面的,而不是最终的SQL执行逻辑。

表达式的处理可以分为常量表达式和动态变量表达式,常量表达式在VS编译的时候就可以直接计算表达式是否是true、false。而动态变量表达式则需要在后期进行表达式解析的时候计算的,换句话说Linq to Provider中的Provider提供程序是具有高智商的表达式执行器,不仅仅是对表达式等价解析中间还夹杂着对表达式解析的自定义逻辑代码。[王清培版权所有,转载请给出署名]

打个比方,我们都有过拼接查询条件的经历,界面上有N个查询条件字段,需要根据用户是否填写了哪个字段进行动态的拼接进LINQ语句中去。一般我们都会进行if的判断才行,因为我们都觉得Where后面的条件表达式是直接被解析成对应逻辑的SQL语句,所以只要拼接进去的都是被解析成SQL的Where子句。由于LINQ是无法拆分开来进行组装的,必须一次写完才能通过编译。所以我们都在使用着查询扩展方法进行数据查询,这样的困境使我们无法看到LINQ的优雅,反而一直用不到。

通过观察LINQPad工具解析的SQL语句,发现LINQ查询表达式在提供程序内部将被执行、解析两个过程,跟VS的过程是一样的,能执行先执行,然后解析,解析是建立在前期执行过后的基础上的。我们还是来看一个比较简单的LINQ解析后的SQL和链式方法;[王清培版权所有,转载请给出署名]

LINQ查询表达式:from truck in TB_CX_TRUCKs where 1==1 ||truck.LICENSE_NUMBER.Length<10 select truck

LINQ等价的链式方法:TB_CX_TRUCKs.Where (truck => (True || (truck.LICENSE_NUMBER.Length < 10)))

图3:

3

对照链式方法,很明显VS先对1==1表达式进行了执行并返回true作为后面整个表达式的一部分拼接进Where链式方法,所以先执行再解析两个过程。然后我们对最后的SQL进行分析,没有看见任何Where语句,为什么呢?是因为提供程序在内部对表达式进行了执行并分析了我们想要的输出结果,也不知道这样的效果是不是为了满足我们多条件拼接的问题。

由于Where方法里面的Lambda表达如果被执行的话,那么将不会执行(truck.LICENSE-NUMBER.Length<10),所以这点为我们的多条件拼接提供了接口。[王清培版权所有,转载请给出署名]

我们看一下多条件组合查询示例:

4

将界面上的查询实体传入到数据访问层之后:

public List<Truck> GetList(Truck truckModel) 

    using (KJtest0817Entities DbContext = new KJtest0817Entities()) 
    { 
        var resultList = from truck in DbContext.TB_CX_TRUCK 
                         where string.IsNullOrEmpty(truckModel.ENGINE_NUMBER) || truck.ENGINE_NUMBER == truckModel.ENGINE_NUMBER 
                         where string.IsNullOrEmpty(truckModel.LICENSE_NUMBER) || truck.ENGINE_NUMBER == truckModel.LICENSE_NUMBER 
                         select new Truck() 
                         { 
                             BRAND = truck.BRAND 
                         }; 
        return resultList.ToList(); 
    } 
}

这样的查询LINQ确实很优美,比起之前的IFELSE判断也省事很多。 

IQueryable<TB_CX_TRUCK> queryList = DbContext.TB_CX_TRUCK.AsQueryable();//初始化一个IQueryable对象 
if (!string.IsNullOrEmpty(truckModel.LICENSE_NUMBER)) 
       queryList = queryList.Where(truck => truck.LICENSE_NUMBER.Contains(truckModel.LICENSE_NUMBER)); 
if (!string.IsNullOrEmpty(truckModel.TRUCK_MODEL_CODE)) 
       queryList = queryList.Where(truck => truck.TRUCK_MODEL_CODE.Contains(truckModel.TRUCK_MODEL_CODE));

如果有很多个查询条件,那么我们将要写很多这样的判断代码,即不方便也不美观。[王清培版权所有,转载请给出署名]

 5

多条件之间的OR查询

尽管很多场合下我们都是使用Linq中的where关键字来拼接查询条件,但是有一种需求Linq查询确实满足不了我们,那就是多条件之间是OR的关系。因为只要我们用Linq或者链式方法出来的写出来的SQL语句中的where条件后面将都是and关系,这个时候我们只能用链式方法来进行拆分才行。

public List<DutyModel> GetList(DutyModel dutyModel) 

    using (UserOrgDemo2Entities Context = new UserOrgDemo2Entities()) 
    { 
        IQueryable<TB_DUTY> result = Context.TB_DUTY.AsQueryable(); 
        System.Linq.Expressions.Expression<Func<TB_DUTY, bool>> expressionDUTY = DynamicLinqExpressions.True<TB_DUTY>();

        if (!(dutyModel.RANK_NO == 0)) 
            expressionDUTY.Or(duty => duty.RANK_NO == dutyModel.RANK_NO); 
        if (!string.IsNullOrEmpty(dutyModel.USER_PRIV)) 
            expressionDUTY.Or(duty => duty.USER_PRIV == dutyModel.USER_PRIV);

       return result.Where(expressionDUTY).Select(tb_duty=>new DutyModel(){ USER_PRIV=tb_duty.USER_PRIV}).ToList(); 
    } 
}

这里有个重点就是老外(估计是比较厉害的前辈,在此谢谢了!)写的一个*.cs文件,里面是Expression<T>表达式文件的扩展方法,主要就是用来进行多条件Or、And之间组合查询用的。

所有说如果多条件组合查询之间是and关系可以直接使用Linq,如果是or或者是or与and一起,那么可以使用上面这种链式查询方法。

总结:其实说了那么多目的只有一个,LINQ的解析过程并非只有一个“提供程序翻译成SQL”的过程,而是包括了两个阶段,四个过程的处理,LINQ的写法很多种,原理应该是差不多的,只要我们在写LINQ的时候综合考虑这几个处理过程,应该对我们应对复杂的查询很有帮助。




 本文转自 王清培 51CTO博客,原文链接:http://blog.51cto.com/wangqingpei557/1132182,如需转载请自行联系原作者



相关文章
|
7天前
|
缓存 安全 PHP
【PHP开发专栏】Symfony框架核心组件解析
【4月更文挑战第30天】本文介绍了Symfony框架,一个模块化且高性能的PHP框架,以其可扩展性和灵活性备受开发者青睐。文章分为三部分,首先概述了Symfony的历史、特点和版本。接着,详细解析了HttpFoundation(处理HTTP请求和响应)、Routing(映射HTTP请求到控制器)、DependencyInjection(管理依赖关系)、EventDispatcher(实现事件驱动编程)以及Security(处理安全和认证)等核心组件。
|
3天前
|
人工智能 自然语言处理 算法
分享几个.NET开源的AI和LLM相关项目框架
分享几个.NET开源的AI和LLM相关项目框架
|
7天前
|
NoSQL 大数据 数据处理
MongoDB聚合框架与复杂查询优化:技术深度解析
【4月更文挑战第30天】本文深入探讨了MongoDB的聚合框架和复杂查询优化技术。聚合框架包含$match、$group、$sort和$project阶段,用于数据处理和分析,提供灵活性和高性能。优化查询涉及创建合适索引、使用聚合框架、简化查询语句、限制返回结果数、避免跨分片查询、只查询所需字段及使用$inc操作符。理解这些技术有助于提升MongoDB在大数据和复杂查询场景下的性能。
|
10天前
|
敏捷开发 开发框架 持续交付
【软件工程】航行敏捷之路:深度解析Scrum框架的精髓
【软件工程】航行敏捷之路:深度解析Scrum框架的精髓
|
12天前
|
SQL 安全 前端开发
Go语言Gin框架安全加固:全面解析SQL注入、XSS与CSRF的解决方案
Go语言Gin框架安全加固:全面解析SQL注入、XSS与CSRF的解决方案
|
14天前
|
人工智能 决策智能 C++
【AI Agent教程】【MetaGPT】案例拆解:使用MetaGPT实现“狼人杀“游戏(1)- 整体框架解析
【AI Agent教程】【MetaGPT】案例拆解:使用MetaGPT实现“狼人杀“游戏(1)- 整体框架解析
125 1
|
23天前
|
SQL API 数据库
python的Django ORM框架深度解析
【4月更文挑战第14天】在Python的Web开发领域,Django无疑是一个备受推崇的框架。它提供了许多强大的工具和功能,使得开发者能够高效、快速地构建出高质量的Web应用。而在Django的众多特性中,ORM(对象关系映射)框架更是其不可或缺的一部分。本文将详细解析Django ORM框架,帮助读者更好地理解和使用它。
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
46 0
|
12天前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
19 0
|
2月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
32 0

推荐镜像

更多