LINQ to SQL *已编译*查询以及它们何时执行
- 作者: 爱上猫的诱惑
- 来源: 51数据库
- 2022-12-13
问题描述
我有以下编译查询.
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.
- C#通过fleck实现wss协议的WebSocket多人Web实时聊天(附源码)
- 团队城市未满足要求:MSBuildTools12.0_x86_Path 存在
- 使用 MSBuild.exe 在发布模式下构建 C# 解决方案
- 当我发布 Web 应用程序时,AfterPublish 脚本不运行
- 构建时 T4 转换的产品仅在下一个构建中使用
- ASP.NET Core Application (.NET Framework) for Windows x64 only error in project.assets.json
- 新的 .csproj 格式 - 如何将整个目录指定为“链接文件"到子目录?
- 如何将条件编译符号(DefineConstants)传递给 msbuild
- MSBuild 支持 Visual Studio 2017 RTM 中的 T4 模板
- NuGet 包还原找不到包,没有源
