LINQ to SQL 执行流程不完整分析 (Update)

[ 2007-08-20 22:23:11 | 作者: yuhen ]
字号: | |
前面已经写过一次,不过感觉还是很混乱,遂决定重写,算是升级版吧。

先准备好试验对象。
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 查询操作分析。

uploads/200708/24_214509_1.png


要是表达式里面附带了条件语句会是什么样子呢?
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 评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=537

这篇日志没有评论。

发表评论
表情图标
[smile] [confused] [cool] [cry]
[eek] [angry] [wink] [sweat]
[lol] [stun] [razz] [redface]
[rolleyes] [sad] [yes] [no]
[heart] [star] [music] [idea]
UBB代码
转换链接
表情图标
悄悄话
用户名:   密码:  
验证码 * 请输入验证码