LinqToDB 源码分析——生成与执行SQL语句
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了LinqToDB 源码分析——生成与执行SQL语句,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含7233字,纯文字阅读大概需要11分钟。
内容图文
![LinqToDB 源码分析——生成与执行SQL语句](/upload/InfoBanner/zyjiaocheng/473/292242cb92d34e5c8842331c49329257.jpg)
对于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()方法。
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接口实例。例如下面
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, 0), ctx, db, expr, ps, mapInfo); }
好,看到这一段代码。我们可以看到他构建一个MapInfo类。记得笔者前面章节的图片有出现过。最后数据库的结果就是通过MapInfo类转化成相关的对象结果。而这边我们还可以看到GetIEnumerable被重新赋值了。为什么说是被重新赋值了。因为在Query<T>类的构造函数里就已经对GetIEnumerable赋值过了。读者们可以在去查看一下。
这个时候我们就是能明白调用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框架的人做一个引导和分析思路的工作。正如上面讲的想要从源码中学习到东西还是要靠自己去分析才行。
LinqToDB 源码分析——生成与执行SQL语句
标签:操作 ram 实例 .com process str finally list dma
本文系统来源:http://www.cnblogs.com/hayasi/p/6111385.html
内容总结
以上是互联网集市为您收集整理的LinqToDB 源码分析——生成与执行SQL语句全部内容,希望文章能够帮你解决LinqToDB 源码分析——生成与执行SQL语句所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。