LinqToDB 源碼分析——生成與執行SQL語句

生成SQL語句的功能能夠算是LinqToDB框架的最後一步。從上一章中咱們能夠知道處理完表達式樹以後,相關生成SQL信息會被保存在一個叫SelectQuery類的實例。有了這個實例咱們就能夠生成對應的SQL語句。想要了解這一步部分的功能就必須從三個方面入手。1、Linq To SQL的機制原理。2、如何生成SQL語句。3、設置映射結果。sql

生成映射表達式


對於Linq To SQL的機制原理在前面的章節裏面已經講過了。這裏筆者提出來主要目標是明確何時觸發。下面的代碼不是看前面的得到Query<T>類實列,而是看後面的GetIEnumerable方法調用。數據庫

 ExpressionQuery<T>類:express

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

記得筆者前面幾個章節中講到最後都會去調用倆個方法分別是Query<T>類中的GetIEnumerable方法和GetElement方法。而這倆個方法都是Func類型。以下app

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

顯然很明顯在調用GetIEnumerable方法必定要知道哪個方法賦給他了。好了,先暫停一下。讓咱們去看一下上一章中筆者講到Build<T>()方法有三個重要方法中的一個——BuildQuery()方法。框架

 1 internal Query<T> Build<T>()
 2 {
 3      var sequence = BuildSequence(new BuildInfo((IBuildContext)null, Expression, new SelectQuery()));
 4 
 5       if (_reorder)
 6            lock (_sync)
 7            {
 8                _reorder = false;
 9                _sequenceBuilders = _sequenceBuilders.OrderByDescending(_ => _.BuildCounter).ToList();
10            }
11 
12        _query.Init(sequence, CurrentSqlParameters);
13 
14        var param = Expression.Parameter(typeof(Query<T>), "info");
15 
16        sequence.BuildQuery((Query<T>)_query, param);
17 
18        return (Query<T>)_query;
19 }

事實在調用GetIEnumerable方法以前,上面的BuildQuery()方法裏面已經對GetIEnumerable進行了賦值一個新的方法。對於BuildQuery()方法只要點擊進去看的話,就會發現他並非屬於XxxxBuilder類的。而是屬於XxxxBuilder類對應的IBuildContext接口實例。例以下面ide

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>類:ui

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, 0), ctx, db, expr, ps, mapInfo);
}

好,看到這一段代碼。咱們能夠看到他構建一個MapInfo類。記得筆者前面章節的圖片有出現過。最後數據庫的結果就是經過MapInfo類轉化成相關的對象結果。而這邊咱們還能夠看到GetIEnumerable被從新賦值了。爲何說是被從新賦值了。由於在Query<T>類的構造函數裏就已經對GetIEnumerable賦值過了。讀者們能夠在去查看一下。this

這個時候咱們就是能明白調用GetIEnumerable方法,事實上是在調用上面代碼中的賦值的Map方法。因此很明顯去看Map方法作什麼就是明白如何調用做者構建方法。便是上面提到的表達式構建的方法。

生成SQL語句


最後必定要執行數據庫,這一步操做離不開上面講到的GetIEnumerable方法。同時咱們也知道GetIEnumerable方法事實上是在調用Map方法。而這個過程當中會用到一個叫PreparedQuery類。這個類就是用於執行數據庫的預查詢類。裏面存放了生成的SQL語句。

Query<T>類:

 1  TE RunQuery<TE>(
 2             QueryContext ctx,
 3             IDataContextInfo dataContextInfo,
 4             Expression expr,
 5             object[] parameters,
 6             Func<QueryContext, IDataContext, IDataReader, Expression, object[], TE> mapper)
 7 {
 8             var dataContext = dataContextInfo.DataContext;
 9 
10             object query = null;
11 
12             try
13             {
14                 query = SetCommand(dataContext, expr, parameters, 0, true);
15 
16                 using (var dr = dataContext.ExecuteReader(query))
17                     while (dr.Read())
18                         return mapper(ctx, dataContext, dr, expr, parameters);
19 
20                 return Array<TE>.Empty.First();
21             }
22             finally
23             {
24                 if (query != null)
25                     dataContext.ReleaseQuery(query);
26 
27                 if (dataContextInfo.DisposeContext)
28                     dataContext.Dispose();
29             }
30 }

上面這段代碼是執行數據庫的入口地方。筆者用紅色標出了執行數據庫以前,所作事情的相關代碼——生成SQL語句。不過咱們會發現query不是一個字符串,而是PreparedQuery類實例。那麼咱們看一下生成PreparedQuery的地方,就能明白生成SQL語句離不開一個叫BasicSqlBuilder類。

DataConnection類:

 1 internal PreparedQuery GetCommand(IQueryContext query)
 2 {
 3      if (query.Context != null)
 4      {
 5           return new PreparedQuery
 6           {
 7                     Commands = (string[])query.Context,
 8                     SqlParameters = query.SelectQuery.Parameters,
 9                     SelectQuery = query.SelectQuery,
10                     QueryHints = query.QueryHints,
11           };
12      }
13 
14      var sql = query.SelectQuery.ProcessParameters();
15      var newSql = ProcessQuery(sql);
16 
17      if (!object.ReferenceEquals(sql, newSql))
18      {
19           sql = newSql;
20          sql.IsParameterDependent = true;
21      }
22 
23      var sqlProvider = DataProvider.CreateSqlBuilder();
24 
25      var cc = sqlProvider.CommandCount(sql);
26      var sb = new StringBuilder();
27 
28      var commands = new string[cc];
29 
30      for (var i = 0; i < cc; i++)
31      {
32           sb.Length = 0;
33 
34           sqlProvider.BuildSql(i, sql, sb);
35           commands[i] = sb.ToString();
36      }
37 
38      if (!query.SelectQuery.IsParameterDependent)
39                 query.Context = commands;
40 
41       return new PreparedQuery
42       {
43                 Commands = commands,
44                 SqlParameters = sql.Parameters,
45                 SelectQuery = sql,
46                 SqlProvider = sqlProvider,
47                 QueryHints = query.QueryHints,
48        };
49 }

上面紅色部分就是生成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框架的人作一個引導和分析思路的工做。正如上面講的想要從源碼中學習到東西仍是要靠本身去分析才行。

相關文章
相關標籤/搜索