¿LINQ-to-SQL admite consultas componibles?
-
01-07-2019 - |
Pregunta
Hablando como programador que no es experto en C#, tengo curiosidad por conocer la semántica de evaluación de consultas LINQ como las siguientes:
var people = from p in Person
where p.age < 18
select p
var otherPeople = from p in people
where p.firstName equals "Daniel"
select p
Asumiendo que Person
es una entidad ADO que define la age
y firstName
campos, ¿qué haría esto desde el punto de vista de la base de datos?Específicamente, ¿la people
La consulta se ejecutará para producir una estructura en memoria, que luego sería consultada por el otherPeople
¿consulta?¿O sería la construcción de otherPeople
simplemente extraiga los datos relacionados con la consulta de people
y luego producir una nueva consulta de base de datos?Entonces, si repito ambas consultas, ¿cuántas declaraciones SQL se ejecutarían?
Solución
Son componibles.Esto es posible porque las consultas LINQ son en realidad expresiones (código como datos), que los proveedores de LINQ como LINQ-to-SQL pueden evaluar y generar el SQL correspondiente.
Debido a que las consultas LINQ se evalúan de forma diferida (p. ej.no se ejecutará hasta que repita los elementos), el código que mostró en realidad no tocará la base de datos.Hasta que no itere sobre otras personas o personas no se generará y ejecutará SQL.
Otros consejos
Sí, la consulta resultante está compuesta.Incluye la cláusula donde completa.Active la creación de perfiles SQL y pruébelo usted mismo.
Linq hace esto a través de árboles de expresión.La primera declaración linq produce un árbol de expresión;no ejecuta la consulta.La segunda declaración linq se basa en el árbol de expresión creado por la primera.La declaración solo se ejecuta cuando enumera la colección resultante.
var people = from p in Person
where p.age < 18
select p
Se traduce a:
SELECT [t0].[PersonId], [t0].[Age], [t0].[FirstName]
FROM [dbo].[Person] AS [t0]
WHERE [t0].[Age] < @p0
donde @p0 se envía como 18
var otherPeople = from p in people
where p.firstName equals "Daniel"
select p
Se traduce a:
SELECT [t0].[PersonId], [t0].[Age], [t0].[FirstName]
FROM [dbo].[Person] AS [t0]
WHERE [t0].[FirstName] = @p0
donde @p0 se envía como "Daniel"
var morePeople = from p1 in people
from p2 in otherPeople
where p1.PersonId == p2.PersonId
select p1;
Se traduce a:
SELECT [t0].[PersonId], [t0].[Age], [t0].[FirstName]
FROM [dbo].[Person] AS [t0], [dbo].[Person] AS [t1]
WHERE ([t0].[PersonId] = [t1].[PersonId]) AND ([t0].[Age] < @p0) AND ([t1].[FirstName] = @p1)
donde @p0 es 18, @p1 es "Daniel"
En caso de duda, llame a ToString() en su IQueryable o proporcione un TextWriter a la propiedad Log de DataContext.
people
y otherPeople
contener objetos de tipo IQueryable<Person>
.
Si itera sobre ambos, por separado, ejecutará dos consultas.Si solo iteras otherPeople
, ejecutará la consulta esperada, con dos cláusulas donde.
Si lo haces .ToList()
en people
y usar el devuelto List<Person>
en la segunda consulta en lugar de personas, se convierte en LINQ-to-Objects y no se ejecuta ningún SQL.
Este comportamiento se conoce como ejecución diferida.Lo que significa que no se realiza ninguna consulta hasta que sea necesaria.Antes de la ejecución, son sólo árboles de expresión que se manipulan para formular la consulta final.
Ambas consultas se ejecutarán cuando intente acceder a los resultados finales.Puede intentar ver el SQL original generado a partir de las propiedades del objeto DataContext.