[英]LINQ to SQL *compiled* queries and when they execute
我有以下編譯的查詢。
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
);
當我這樣做時,它會立即在數據庫上執行
var db = new Db()
var query = func(db, 5); // Query hits the database here
和之前一樣
var result = query.SingleOrDefault(); // Happens in memory
但是如果這個查詢沒有被編譯,如
var query = from i in db.Items
where i.ID == id
select i
然后它在完成后在數據庫上執行
var result = query.SingleOrDefault();
這是預期的行為嗎?
注意:這是返回 IQueryable 的已編譯查詢何時執行的副本? ,但那里的所有答案似乎都不同意我的發現。 我已經在那里發布了我的答案,但我不知道如何引起人們的注意,因為它已經超過 2 年了。
有趣的問題。 將其帶到反編譯的源代碼中,當您編譯查詢時,會發生以下情況:
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 方法定義如下:
private static bool UseExpressionCompile(LambdaExpression query)
{
return typeof (ITable).IsAssignableFrom(query.Body.Type);
}
這對於您定義的表達式的計算結果為 false,因此使用 else 情況。
調用是這樣的:
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 就像:
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 class,SqlProvider.CompiledQuery 是實現 ICompiledQuery 的 class。 在 class 上執行:
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,這是一個相當大的方法,所以我將發布亮點:
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 命令。 所以我會說你是對的。 與普遍的看法相反,在延遲執行方面,已編譯查詢的行為與未編譯查詢的行為不同。
我很確定你可以從 MS 下載實際的源代碼,但我手邊沒有它,而且 Resharper 6 有一個很棒的 go 可以反編譯 function,所以我只是使用它。
除了這個,我沒有什么可以添加到 Andrew Barrett 的回答中:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.