生成SQL语句的功能可以算是LinqToDB框架的最后一步。从上一章中我们可以知道处理完表达式树之后,相关生成SQL信息会被保存在一个叫SelectQuery类的实例。有了这个实例我们就可以生成对应的SQL语句。想要了解这一步部分的功能就必须从三个方面入手。一、Linq To SQL的机制原理。二、如何生成SQL语句。三、设置映射结果。

生成映射表达式


对于Linq To SQL的机制原理在前面的章节里面已经讲过了。这里笔者提出来主要目标是明确什么时候触发。下面的代码不是看前面的获得Query<T>类实列,而是看后面的GetIEnumerable方法调用。

ExpressionQuery<T>类:

IEnumerable<T> Execute(IDataContextInfo dataContextInfo, Expression expression)
{
return GetQuery(expression, true).GetIEnumerable(null, dataContextInfo, expression, Parameters);
}

记得笔者前面几个章节中讲到最后都会去调用俩个方法分别是Query<T>类中的GetIEnumerable方法和GetElement方法。而这俩个方法都是Func类型。如下

public Func<QueryContext, IDataContextInfo, Expression, object[], object> GetElement;
public Func<QueryContext, IDataContextInfo, Expression, object[], IEnumerable<T>> GetIEnumerable;

显然很明显在调用GetIEnumerable方法一定要知道哪一个方法赋给他了。好了,先暂停一下。让我们去看一下上一章中笔者讲到Build<T>()方法有三个重要方法中的一个——BuildQuery()方法。

 internal Query<T> Build<T>()
{
var sequence = BuildSequence(new BuildInfo((IBuildContext)null, Expression, new SelectQuery())); if (_reorder)
lock (_sync)
{
_reorder = false;
_sequenceBuilders = _sequenceBuilders.OrderByDescending(_ => _.BuildCounter).ToList();
} _query.Init(sequence, CurrentSqlParameters); var param = Expression.Parameter(typeof(Query<T>), "info"); sequence.BuildQuery((Query<T>)_query, param); return (Query<T>)_query;
}

事实在调用GetIEnumerable方法之前,上面的BuildQuery()方法里面已经对GetIEnumerable进行了赋值一个新的方法。对于BuildQuery()方法只要点击进去看的话,就会发现他并不是属于XxxxBuilder类的。而是属于XxxxBuilder类对应的IBuildContext接口实例。例如下面

TableContext类:

 public void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter)
{
var expr = BuildQuery(typeof(T), this, null);
var mapper = Builder.BuildMapper<T>(expr); query.SetQuery(mapper);
}

好像没有发现对于GetIEnumerable进行赋值的代码。不要紧张我们先看一下这代码是做什么的。假设我们已经生成SQL语句,也执行了数据库了。那么得到数据库的结果又是什么样子映射成对象类呢?看样子大家一定明白笔者的意思。没有错。这边就是设置回返结果的映射。同样子作者也是用表达式来构建一个方法来设置返回对象结果。具体做法读者可以自己断点进去看。mapper就是最后生成的映射表达式树。我们可以看到他做为参数传给了SetQuery()方法。

Query<T>类:

internal void SetQuery(Expression<Func<QueryContext, IDataContext, IDataReader, Expression, object[], T>> expression)
{
var query = GetQuery();
var mapInfo = new MapInfo { Expression = expression }; ClearParameters(); GetIEnumerable = (ctx, db, expr, ps) => Map(query(db, expr, ps, ), ctx, db, expr, ps, mapInfo);
}

好,看到这一段代码。我们可以看到他构建一个MapInfo类。记得笔者前面章节的图片有出现过。最后数据库的结果就是通过MapInfo类转化成相关的对象结果。而这边我们还可以看到GetIEnumerable被重新赋值了。为什么说是被重新赋值了。因为在Query<T>类的构造函数里就已经对GetIEnumerable赋值过了。读者们可以在去查看一下。

这个时候我们就是能明白调用GetIEnumerable方法,事实上是在调用上面代码中的赋值的Map方法。所以很明显去看Map方法做什么就是明白如何调用作者构建方法。即是上面提到的表达式构建的方法。

生成SQL语句


最后一定要执行数据库,这一步操作离不开上面讲到的GetIEnumerable方法。同时我们也知道GetIEnumerable方法事实上是在调用Map方法。而这个过程中会用到一个叫PreparedQuery类。这个类就是用于执行数据库的预查询类。里面存放了生成的SQL语句。

Query<T>类:

  TE RunQuery<TE>(
QueryContext ctx,
IDataContextInfo dataContextInfo,
Expression expr,
object[] parameters,
Func<QueryContext, IDataContext, IDataReader, Expression, object[], TE> mapper)
{
var dataContext = dataContextInfo.DataContext; object query = null; try
{
query = SetCommand(dataContext, expr, parameters, 0, true); using (var dr = dataContext.ExecuteReader(query))
while (dr.Read())
return mapper(ctx, dataContext, dr, expr, parameters); return Array<TE>.Empty.First();
}
finally
{
if (query != null)
dataContext.ReleaseQuery(query); if (dataContextInfo.DisposeContext)
dataContext.Dispose();
}
}

上面这段代码是执行数据库的入口地方。笔者用红色标出了执行数据库之前,所做事情的相关代码——生成SQL语句。不过我们会发现query不是一个字符串,而是PreparedQuery类实例。那么我们看一下生成PreparedQuery的地方,就能明白生成SQL语句离不开一个叫BasicSqlBuilder类。

DataConnection类:

 internal PreparedQuery GetCommand(IQueryContext query)
{
if (query.Context != null)
{
return new PreparedQuery
{
Commands = (string[])query.Context,
SqlParameters = query.SelectQuery.Parameters,
SelectQuery = query.SelectQuery,
QueryHints = query.QueryHints,
};
} var sql = query.SelectQuery.ProcessParameters();
var newSql = ProcessQuery(sql); if (!object.ReferenceEquals(sql, newSql))
{
sql = newSql;
sql.IsParameterDependent = true;
} var sqlProvider = DataProvider.CreateSqlBuilder(); var cc = sqlProvider.CommandCount(sql);
var sb = new StringBuilder(); var commands = new string[cc]; for (var i = ; i < cc; i++)
{
sb.Length = ; sqlProvider.BuildSql(i, sql, sb);
commands[i] = sb.ToString();
} if (!query.SelectQuery.IsParameterDependent)
query.Context = commands; return new PreparedQuery
{
Commands = commands,
SqlParameters = sql.Parameters,
SelectQuery = sql,
SqlProvider = sqlProvider,
QueryHints = query.QueryHints,
};
}

上面红色部分就是生成SQL语句相关的代码问部分。对于BasicSqlBuilder类笔者简单的做一些介绍。做一个初步的了解。想要更深入的了解。最好自己去查看一下代码。BasicSqlBuilder类根据DML来进行划分的。所以我们可以看到下列方法

1.BuildSelectQuery方法:构建查询语句。
2.BuildDeleteQuery方法:构建删除语句。
3.BuildUpdateQuery方法:构建更新语句。
4.BuildInsertQuery方法:构建插入语句。
等等

同时又依据SQL语句的结果分为以下方法。

1.BuildSelectClause:关键字SELECT部分的语句。
2.BuildFromClause:关键字FROM部分的语句。
3.BuildWhereClause:关键字WHERE部分的语句。
4.BuildGroupByClause:关键字GROUPBY部分的语句。
等等

BasicSqlBuilder类事实上笔者认为比较简单。而且笔者的目地都是在引导一种查看源码的思路。想要从源码中学习到东西还是要靠自己去分析才行。

结语句


本系列的文章笔者也只能引导到这里了。笔者对本系列的定位就是帮助想要了解LinqToDB框架的人做一个引导和分析思路的工作。正如上面讲的想要从源码中学习到东西还是要靠自己去分析才行。

LinqToDB 源码分析——生成与执行SQL语句的更多相关文章

  1. Python 一键拉取Git分支源码自动解析并执行SQL语句

    基于Python实现自动拉取Git分支源码自动解析并执行SQL语句 by:授客 QQ:1033553122 1.代码用途 开发过程中,研发人员会提交SQL更新脚本到Git源码库,然后测试负责去拉取这些 ...

  2. LinqToDB 源码分析——生成表达式树

    当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开I ...

  3. Mybatis源码分析之Mapper执行SQL过程(三)

    上两篇已经讲解了SqlSessionFactory的创建和SqlSession创建过程.今天我们来分析myabtis的sql是如何一步一步走到Excutor. 还是之前的demo    public  ...

  4. Yii2 源码分析 入口文件执行流程

    Yii2 源码分析  入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...

  5. 精尽 MyBatis 源码分析 - SqlSession 会话与 SQL 执行入口

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  6. 一个由正则表达式引发的血案 vs2017使用rdlc实现批量打印 vs2017使用rdlc [asp.net core 源码分析] 01 - Session SignalR sql for xml path用法 MemCahe C# 操作Excel图形——绘制、读取、隐藏、删除图形 IOC,DIP,DI,IoC容器

    1. 血案由来 近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求:1. 英文字母大小写2. 数字3. 越南文4. 一些特殊字符,如“&”,“- ...

  7. springMVC源码分析--访问请求执行ServletInvocableHandlerMethod和InvocableHandlerMethod

    在之前一篇博客中springMVC源码分析--RequestMappingHandlerAdapter(五)我们已经简单的介绍到具体请求访问的执行某个Controller中的方法是在RequestMa ...

  8. Spark 源码分析 -- task实际执行过程

    Spark源码分析 – SparkContext 中的例子, 只分析到sc.runJob 那么最终是怎么执行的? 通过DAGScheduler切分成Stage, 封装成taskset, 提交给Task ...

  9. mybatis源码分析(五)------------SQL的执行过程

    在对SQL的执行过程进行分析前,先看下测试demo: /** * @author chenyk * @date 2018年8月20日 */ public class GoodsDaoTest { pr ...

随机推荐

  1. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  2. 反应器(Reactor)和主动器(Proactor)

    网络方面用的比较多的库是libevent和boost.asio,两者都是跨平台的.其中libevent是基于Reactor实现的,而boost.asio是基于Proactor实现的.Reactor和P ...

  3. ABP项目中使用Swagger生成动态WebAPI

    本文是根据角落的白板报的<使用ABP实现SwaggerUI,生成动态webapi>一文的学习总结,感谢原文作者角落的白板报. 1 安装Swashbuckle.core 1.1 选择WebA ...

  4. 极光推送和友盟推送,ios端和安卓端的后端调试设置

    我是最后端的,这两天搞了一个app项目,前端安卓使用友盟很方便,调试比较顺利,然后ios就遇到各种问题了,证书.发送成功推送不成功,测试时用的TestMode(),ios上架之后就必须用product ...

  5. MySQL:常见使用问题

    内容 1.Linux 上安装 MySQL 2.单机上安装多实例 3.不知root密码情况下,修改root密码 1.Linux 上安装MySQL 安装步骤: 1)解压 tar.gz文件 -linux-g ...

  6. 我设计的ASP.NET笔试题,你会多少呢

    本笔试题考查范围包括面向对象基础.HTML.CSS.JS.EF.jQuery.SQL.编码思想.算法等范围. 第1题:接口和抽象类有何区别? 第2题:静态方法和实例方法有何区别? 第3题:什么是多态? ...

  7. 猫哥网络编程系列:详解 BAT 面试题

    从产品上线前的接口开发和调试,到上线后的 bug 定位.性能优化,网络编程知识贯穿着一个互联网产品的整个生命周期.不论你是前后端的开发岗位,还是 SQA.运维等其他技术岗位,掌握网络编程知识均是岗位的 ...

  8. Linux 中的数值计算和符号计算

    不知道经常需要做科学计算的朋友们有没有这样的好奇:在 Linux 系统下使用什么工具呢?说到科学计算,首先想到的肯定是 Matlab,如果再说到符号计算,那就非 Mathematica 不可了.可惜, ...

  9. 让tomcat支持中文cookie

    这的确是一个不正常的需求,按照规范,开发者需要将cookie进行编码,因为tomcat不支持中文cookie. 但有时候,你不得不面对这样的情况,比如请求是由他人开发的软件,比如,浏览器控件发出的. ...

  10. 在ABP中创建Person实体类

    经过之前的准备目前我们的项目,终于可以搞正式的开发工作了. 创建实体Person 在Core类库中添加Person类 /// <summary> /// 联系人 /// </summ ...