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

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

到目前为止咱们对LINQ的执行原理已经很清楚了,从它的前期构想到它真正为咱们所用都有足够的证据,可是彷佛问题并无咱们想的那么简单,问题老是在咱们使用中频频出现尤为是新技术的使用,固然有问题才能有进步。[王清培版权全部,转载请给出署名]ide

一: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工具看一下便知;3d

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

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

图1:接口

1

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

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

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的时候综合考虑这几个处理过程,应该对咱们应对复杂的查询颇有帮助。[王清培版权全部,转载请给出署名]

相关文章
相关标签/搜索