Interesante pregunta. Si lo toma a las fuentes descompilados, cuando se compila una consulta, esto es lo que sucede:
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>);
}
El método UseExpressionCompile se define así:
private static bool UseExpressionCompile(LambdaExpression query)
{
return typeof (ITable).IsAssignableFrom(query.Body.Type);
}
Esto da como resultado false para la expresión que haya definido , entonces se usa el caso else
La invocación es así:
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 es como:
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;
}
En este caso, nuestro proveedor es la clase SqlProvider, la SqlProvider.CompiledQuery es la clase que implementa ICompiledQuery.Ejecutar dicha clase se implementa:
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 llama SqlProvider.Execute, que es un método bastante grande, así que voy a publicar los aspectos más destacados:
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);
}
}
En medio de adquisición y liberación del conexión excede los comandos sql. Entonces yo diría que tienes razón. Contrariamente a la creencia popular, las consultas compiladas no se comportan igual que las consultas no compiladas cuando se trata de ejecución diferida.
Estoy bastante seguro de que puedes descargar el código fuente real de MS, pero no lo tengo a mano y Resharper 6 tiene una increíble función de descompilación, así que acabo de usar eso.
Tienes razón. Las consultas compiladas no se pueden componer, devuelven resultados cuando se invoca el delegado, no la consulta en sí. He cambiado mi respuesta a la pregunta referenciada. – alex