LINQ to SQL *已编译*查询以及它们何时执行 [英] LINQ to SQL *compiled* queries and when they execute

查看:16
本文介绍了LINQ to SQL *已编译*查询以及它们何时执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下编译查询.

private static Func<Db, int, IQueryable<Item>> func =
        CompiledQuery.Compile((Db db, int id) => 
            from i in db.Items
            where i.ID == id
            select i
            );

当我这样做时,这会立即在数据库上执行

This executes on the database immediately when I do

var db = new Db()
var query = func(db, 5);  // Query hits the database here

之前

var result = query.SingleOrDefault(); // Happens in memory

但是如果这个查询没有被编译,就像在

But if this query wasn't compiled, as in

var query = from i in db.Items
            where i.ID == id
            select i

然后它在数据库之后执行

   var result = query.SingleOrDefault();

这是预期的行为吗?

注意:这是返回 IQueryable 的编译查询何时执行?,但那里的所有答案似乎都与我的发现不一致.我已经在那里发布了我的答案,但我不知道如何引起人们的注意,因为它已经超过 2 年了.

Note: This is a duplicate of When does a compiled query that returns an IQueryable execute?, but all the answers on there seem to disagree with my findings. I have posted my answer there, but I don't know how to get peoples' attention to it as it's over 2 years old.

推荐答案

有趣的问题.将其带入反编译源,当您编译查询时,会发生以下情况:

Interesting question. Taking it to decompiled sources, when you compile a query, this is what happens:

public static Func<TArg0, TArg1, TResult> Compile<TArg0, TArg1, TResult>(Expression<Func<TArg0, TArg1, TResult>> query) where TArg0 : DataContext
{
  if (query == null)
    System.Data.Linq.Error.ArgumentNull("query");
  if (CompiledQuery.UseExpressionCompile((LambdaExpression) query))
    return query.Compile();
  else
    return new Func<TArg0, TArg1, TResult>(new CompiledQuery((LambdaExpression) query).Invoke<TArg0, TArg1, TResult>);
}

UseExpressionCompile 方法定义如下:

The UseExpressionCompile method is defined like this:

private static bool UseExpressionCompile(LambdaExpression query)
{
  return typeof (ITable).IsAssignableFrom(query.Body.Type);
}

对于您定义的表达式,它的计算结果为 false,因此使用 else 情况.

This evaluates to false for the expression you've defined, so the else case is used.

调用是这样的:

private TResult Invoke<TArg0, TArg1, TResult>(TArg0 arg0, TArg1 arg1) where TArg0 : DataContext
{
  return (TResult) this.ExecuteQuery((DataContext) arg0, new object[2]
  {
    (object) arg0,
    (object) arg1
  });
}

ExecuteQuery 就像:

The ExecuteQuery is like:

private object ExecuteQuery(DataContext context, object[] args)
{
  if (context == null)
    throw System.Data.Linq.Error.ArgumentNull("context");
  if (this.compiled == null)
  {
    lock (this)
    {
      if (this.compiled == null)
        this.compiled = context.Provider.Compile((Expression) this.query);
    }
  }
  return this.compiled.Execute(context.Provider, args).ReturnValue;
}

在这种情况下,我们的提供者是 SqlProvider 类,SqlProvider.CompiledQuery 是实现 ICompiledQuery 的类.在该类上执行执行:

In this case our provider is the SqlProvider class, the SqlProvider.CompiledQuery is the class that implements ICompiledQuery. Execute on that class is implemented:

  public IExecuteResult Execute(IProvider provider, object[] arguments)
  {
    if (provider == null)
      throw System.Data.Linq.SqlClient.Error.ArgumentNull("provider");
    SqlProvider sqlProvider = provider as SqlProvider;
    if (sqlProvider == null)
      throw System.Data.Linq.SqlClient.Error.ArgumentTypeMismatch((object) "provider");
    if (!SqlProvider.CompiledQuery.AreEquivalentShapes(this.originalShape, sqlProvider.services.Context.LoadOptions))
      throw System.Data.Linq.SqlClient.Error.CompiledQueryAgainstMultipleShapesNotSupported();
    else
      return sqlProvider.ExecuteAll(this.query, this.queryInfos, this.factory, arguments, this.subQueries);
  }

SqlProvider.ExecuteAll 调用SqlProvider.Execute,这是一个相当大的方法,所以我将重点发布:

SqlProvider.ExecuteAll calls SqlProvider.Execute, which is a pretty big method, so I'll post the highlights:

private IExecuteResult Execute(Expression query, SqlProvider.QueryInfo queryInfo, IObjectReaderFactory factory, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries, object lastResult)
{
  this.InitializeProviderMode();
  DbConnection dbConnection = this.conManager.UseConnection((IConnectionUser) this);
  try
  {
    DbCommand command = dbConnection.CreateCommand();
    command.CommandText = queryInfo.CommandText;
    command.Transaction = this.conManager.Transaction;
    command.CommandTimeout = this.commandTimeout;
    this.AssignParameters(command, queryInfo.Parameters, userArgs, lastResult);
    this.LogCommand(this.log, command);
    ++this.queryCount;
    switch (queryInfo.ResultShape)
    {
      case SqlProvider.ResultShape.Singleton:
        DbDataReader reader1 = command.ExecuteReader();
...
      case SqlProvider.ResultShape.Sequence:
        DbDataReader reader2 = command.ExecuteReader();
...
      default:
        return (IExecuteResult) new SqlProvider.ExecuteResult(command, queryInfo.Parameters, (IObjectReaderSession) null, (object) command.ExecuteNonQuery(), true);
    }
  }
  finally
  {
    this.conManager.ReleaseConnection((IConnectionUser) this);
  }
}

在获取和释放连接之间,它会执行 sql 命令.所以我会说你是对的.与普遍看法相反,在延迟执行方面,编译查询与未编译查询的行为不同.

In between acquiring and releasing the connection it exceutes sql commands. So I'd say you're right. Contrary to popular belief, compiled queries don't behave the same as uncompiled queries when it comes to deferred execution.

我很确定你可以从 MS 下载实际的源代码,但我手头没有它,而且 Resharper 6 有一个很棒的反编译功能,所以我只是使用了它.

I'm pretty sure you can download the actual source code from MS, but I don't have it handy and Resharper 6 has an awesome go to decompiled function, so I just used that.

这篇关于LINQ to SQL *已编译*查询以及它们何时执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆