LINQ to SQL 执行流程不完整分析 (Update)
[ 2007-08-20 22:23:11 | 作者: yuhen ]
前面已经写过一次,不过感觉还是很混乱,遂决定重写,算是升级版吧。
先准备好试验对象。
用 Dis# 看看反编译结果 (反编译代码经手工整理,以便于阅读)。
这回比较有趣,我们并没有看到我们在另一篇文章 《LINQ to Object 执行流程不完整分析》中看到的那些自动生成委托字段和方法。另外一个不同是 Queryable 替代 Enumerable,而返回的结果也从 IEnumerable<T> 变成了 IQueryable<T>。这些不同让我们从一开始就面临完全不同的路途,有趣~~~~~
DLinq 表达式一如既往,这次编译成了 Lambda Expression,然后调用 Queryable 中定义的扩展方法。在继续分析之前,我们借助于 VS2008 的调试窗口,先记录几个下面会用到的数据。
context.Provider : System.Data.Linq.Provider.IProvider {System.Data.Linq.SqlClient.SqlProvider}
context.Users.Provider : System.Linq.IQueryProvider {System.Data.Linq.Table<User>}
首先,我们看看 Queryable.Select。
创建一个方法调用的 Lambda Expression,并将其传递给我们上面所记录下来的 Table<User>.CreateQuery()。那么我们就跟到 Table<TEntity> 里面看看究竟。
OK! 新的对象出现,DataQuery~~~~~~
到这一步,我们只是获得了一个包含 context 和 Lambda Expression 的对象。那么接下来会如何呢?在我们的例子中我们使用 foreach 来枚举结果,那么自然触发了 GetEnumerator() 这个 "机会"。
书签1 (待会要回来~~~)
context.Provider?也就是我们在前面记录下来的 System.Data.Linq.SqlClient.SqlProvider。至此总算有了点眉目,毕竟 LINQ to SQL 归根结底还是要走到 ADO.NET 这一步的。
Execute -> ExecuteAll -> Execute,跳转到最后,我们终于看到了熟悉的 ADO.NET。数据库的操作结果被存储到 ExecuteResult.ReturnValue 中。
我们回到 "书签1" 位置,在 DataQuery<T>.GetEnumerator() 中,会将这个 IExecuteResult.ReturnValue 转型成 IEnumerable<User>,这样我们就可以枚举其包含的实体对象了。转得头晕脑胀,总算完成了一个简单的 LINQ to SQL 查询操作分析。
要是表达式里面附带了条件语句会是什么样子呢?
反编译结果 (反编译代码经手工整理,以便于阅读)
复杂的 Lambda Expression 构造过程,还有扩展方法也从 Queryable.Select() 变成了 Queryable.Where()。
接下来的流程就和上面的基本相同了。
-------------晕乎中的分割线----------------
本文只是简单地分析了一下 LINQ to SQL 的执行流程,便于我们初步了解这些 "魔法" 背后所隐藏的实现方式。至于具体的实现方法,那就是另外一件事,超出本文范围了。
评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=537
先准备好试验对象。
class Program
{
static void Main(string[] args)
{
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
var q = from u in context.Users select u;
foreach (var user in q)
{
Console.WriteLine(user.Name);
}
}
}
}用 Dis# 看看反编译结果 (反编译代码经手工整理,以便于阅读)。
internal class Program
{
public Program()
{
}
public static void Main(string[] args)
{
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(User), "u");
ParameterExpression[] parameterExpressionArr = new ParameterExpression[] { parameterExpression };
IQueryable<User> q = Queryable.Select<User,User>(
context.Users,
Expression.Lambda<Table<User>>(parameterExpression, parameterExpressionArr));
foreach (User user in q)
{
Console.WriteLine(user.Name);
}
}
}
} // class Program这回比较有趣,我们并没有看到我们在另一篇文章 《LINQ to Object 执行流程不完整分析》中看到的那些自动生成委托字段和方法。另外一个不同是 Queryable 替代 Enumerable,而返回的结果也从 IEnumerable<T> 变成了 IQueryable<T>。这些不同让我们从一开始就面临完全不同的路途,有趣~~~~~
DLinq 表达式一如既往,这次编译成了 Lambda Expression,然后调用 Queryable 中定义的扩展方法。在继续分析之前,我们借助于 VS2008 的调试窗口,先记录几个下面会用到的数据。
context.Provider : System.Data.Linq.Provider.IProvider {System.Data.Linq.SqlClient.SqlProvider}
context.Users.Provider : System.Linq.IQueryProvider {System.Data.Linq.Table<User>}
首先,我们看看 Queryable.Select。
public static class Queryable
{
[Extension]
public static IQueryable<TSource> Select<TSource,TResult>(IQueryable<TSource> source,
Expression<Func<TSource,TResult>> selector)
{
if (source == null)
throw Error.ArgumentNull("source");
if (selector == null)
throw Error.ArgumentNull("selector");
Type[] typeArr = new Type[] {
typeof(TSource),
typeof(TResult)
};
Expression[] expressionArr = new Expression[] {
source.Expression,
Expression.Quote(selector)
};
return source.Provider.CreateQuery<TResult>(
Expression.Call(null,
((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeArr),
expressionArr));
}
}创建一个方法调用的 Lambda Expression,并将其传递给我们上面所记录下来的 Table<User>.CreateQuery()。那么我们就跟到 Table<TEntity> 里面看看究竟。
public sealed class Table<TEntity> : IQueryable<TEntity>, IQueryProvider, IEnumerable<TEntity>,
ITable, IQueryable, IEnumerable, IListSource where TEntity: class
{
IQueryable<TResult> IQueryProvider.CreateQuery<TResult>(Expression expression)
{
if (expression == null)
{
throw Error.ArgumentNull("expression");
}
if (!typeof(IQueryable<TResult>).IsAssignableFrom(expression.Type))
{
throw Error.ExpectedQueryableArgument("expression", typeof(IEnumerable<TResult>));
}
return new DataQuery<TResult>(this.context, expression);
}
}OK! 新的对象出现,DataQuery~~~~~~
internal sealed class DataQuery<T> : IOrderedQueryable<T>, IQueryable<T>, IQueryProvider,
IEnumerable<T>, IOrderedQueryable, IQueryable, IEnumerable, IListSource
{
// ...
public DataQuery(DataContext context, Expression expression)
{
this.context = context;
this.queryExpression = expression;
}
IEnumerator<T> IEnumerable<T>.GetEnumerator();
// ...
}到这一步,我们只是获得了一个包含 context 和 Lambda Expression 的对象。那么接下来会如何呢?在我们的例子中我们使用 foreach 来枚举结果,那么自然触发了 GetEnumerator() 这个 "机会"。
书签1 (待会要回来~~~)
internal sealed class DataQuery<T> : IOrderedQueryable<T>, IQueryable<T>, IQueryProvider,
IEnumerable<T>, IOrderedQueryable, IQueryable, IEnumerable, IListSource
{
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>) context.Provider.Execute(queryExpression).ReturnValue).GetEnumerator();
}
}context.Provider?也就是我们在前面记录下来的 System.Data.Linq.SqlClient.SqlProvider。至此总算有了点眉目,毕竟 LINQ to SQL 归根结底还是要走到 ADO.NET 这一步的。
public class SqlProvider : IReaderProvider, IProvider, IDisposable, IConnectionUser,
ISqlSubQueryCompiler
{
IExecuteResult IProvider.Execute(Expression query)
{
// ...
// 决定用 Sql2000Provider 还是 Sql2005Provider。
this.InitializeProviderMode();
// ...
LambdaExpression expression = query as LambdaExpression;
if (expression != null)
{
query = expression.Body;
}
IObjectReaderFactory readerFactory = null;
QueryInfo info = queryInfos[queryInfos.Length - 1];
if (info.ResultShape == ResultShape.Singleton)
{
readerFactory = this.GetReaderFactory(info.Query, info.ResultType);
}
else if (info.ResultShape == ResultShape.Sequence)
{
readerFactory = this.GetReaderFactory(info.Query, TypeSystem.GetElementType(info.ResultType));
}
return this.ExecuteAll(query, queryInfos, readerFactory, null);
}
private IExecuteResult ExecuteAll(Expression query, QueryInfo[] queryInfos,
IObjectReaderFactory factory, object[] userArguments)
{
IExecuteResult result = null;
object lastResult = null;
int index = 0;
int length = queryInfos.Length;
while (index < length)
{
if (index < (length - 1))
{
result = this.Execute(query, queryInfos[index], null, null, userArguments, lastResult);
}
else
{
result = this.Execute(query, queryInfos[index], factory, null, userArguments, lastResult);
}
if (queryInfos[index].ResultShape == ResultShape.Return)
{
lastResult = result.ReturnValue;
}
index++;
}
return result;
}
private IExecuteResult Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory,
object[] parentArgs, object[] userArgs, object lastResult)
{
IExecuteResult result3;
this.InitializeProviderMode();
DbConnection connection = this.conManager.UseConnection(this);
try
{
DbCommand cmd = connection.CreateCommand();
cmd.CommandText = queryInfo.CommandText;
// ...
// 从数据库获取数据
DbDataReader reader3 = cmd.ExecuteReader();
IObjectReader reader4 = factory.Create(reader3, true, this, parentArgs, userArgs);
// ...
// 利用反射创建泛型集合对象,也就是 "SqlProvider.OneTimeEnumerable<User>" 。
IEnumerable source = (IEnumerable)Activator.CreateInstance(
typeof(OneTimeEnumerable<>).MakeGenericType(
new Type[] { TypeSystem.GetElementType(queryInfo.ResultType) }),
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null,
new object[] { reader4 },
null);
// ...
result.ReturnValue = source;
// 设置返回对象
result3 = result;
}
finally
{
this.conManager.ReleaseConnection(this);
}
return result3;
}
}Execute -> ExecuteAll -> Execute,跳转到最后,我们终于看到了熟悉的 ADO.NET。数据库的操作结果被存储到 ExecuteResult.ReturnValue 中。
public interface IExecuteResult : IDisposable
{
// Methods
object GetParameterValue(int parameterIndex);
// Properties
object ReturnValue { get; }
}
private class ExecuteResult : IExecuteResult, IDisposable
{
}我们回到 "书签1" 位置,在 DataQuery<T>.GetEnumerator() 中,会将这个 IExecuteResult.ReturnValue 转型成 IEnumerable<User>,这样我们就可以枚举其包含的实体对象了。转得头晕脑胀,总算完成了一个简单的 LINQ to SQL 查询操作分析。
要是表达式里面附带了条件语句会是什么样子呢?
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
var q = from u in context.Users where u.Name == "user1" select u;
foreach (var user in q)
{
Console.WriteLine(user.Name);
}
}反编译结果 (反编译代码经手工整理,以便于阅读)
using (DataClasses1DataContext context = new DataClasses1DataContext())
{
MethodInfo getName = (MethodInfo)methodof(User.get_Name);
MethodInfo equality = (MethodInfo)methodof(string.op_Equality);
ParameterExpression paramter = Expression.Parameter(typeof(User), "u");
ConstantExpression constant = Expression.Constant("user1", typeof(string));
MemberExpression property = Expression.Property(paramter, getName);
BinaryExpression equal = Expression.Equal(property, constant, false, equality);
var lambda = Expression.Lambda<Func<User, bool>>(equal, paramter);
IQueryable<User> q = context.Users.Where<User>(lambda);
foreach (User user in q)
{
Console.WriteLine(user.Name);
}
}复杂的 Lambda Expression 构造过程,还有扩展方法也从 Queryable.Select() 变成了 Queryable.Where()。
public static class Queryable
{
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
if (predicate == null)
{
throw Error.ArgumentNull("predicate");
}
return source.Provider.CreateQuery<TSource>(
Expression.Call(null,
((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] {typeof(TSource)}),
new Expression[] { source.Expression, Expression.Quote(predicate) }));
}
}接下来的流程就和上面的基本相同了。
-------------晕乎中的分割线----------------
本文只是简单地分析了一下 LINQ to SQL 的执行流程,便于我们初步了解这些 "魔法" 背后所隐藏的实现方式。至于具体的实现方法,那就是另外一件事,超出本文范围了。
[最后修改由 yuhen, 于 2007-08-24 21:45:28]
评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=537
这篇日志没有评论。







