Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Language Integrated Query (LINQ) contiene muchos operadores complejos, que combinan varios orígenes de datos o realiza un procesamiento complejo. No todos los operadores LINQ tienen traducciones adecuadas en el servidor. A veces, una consulta de un formulario se traduce en el servidor, pero si se escribe en otro formulario no se traduce aunque el resultado sea el mismo. En esta página se describen algunos de los operadores complejos y sus variaciones admitidas. En futuras versiones, es posible que reconozcamos más patrones y agreguemos sus traducciones correspondientes. También es importante tener en cuenta que la compatibilidad con la traducción varía entre proveedores. Es posible que una consulta determinada, que se traduce en SqlServer, no funcione para las bases de datos de SQLite.
Sugerencia
Puede ver un ejemplo de este artículo en GitHub.
Unirse
El operador LINQ Join permite conectar dos orígenes de datos basados en el selector de claves de cada origen, generando una tupla de valores cuando la clave coincide. Se traduce de forma natural en INNER JOIN
bases de datos relacionales. Aunque la combinación LINQ tiene selectores de claves externos e internos, la base de datos requiere una única condición de combinación. Por lo tanto, EF Core genera una condición de combinación comparando el selector de teclas externa con el selector de claves interno para obtener igualdad.
var query = from photo in context.Set<PersonPhoto>()
join person in context.Set<Person>()
on photo.PersonPhotoId equals person.PhotoId
select new { person, photo };
SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON [p0].[PersonPhotoId] = [p].[PhotoId]
Además, si los selectores de claves son tipos anónimos, EF Core genera una condición de combinación para comparar la igualdad de componentes.
var query = from photo in context.Set<PersonPhoto>()
join person in context.Set<Person>()
on new { Id = (int?)photo.PersonPhotoId, photo.Caption }
equals new { Id = person.PhotoId, Caption = "SN" }
select new { person, photo };
SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]
FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON ([p0].[PersonPhotoId] = [p].[PhotoId] AND ([p0].[Caption] = N'SN'))
GroupJoin
El operador LINQ GroupJoin permite conectar dos orígenes de datos similares a Join, pero crea un grupo de valores internos para buscar elementos externos coincidentes. Ejecutar una consulta como en el ejemplo siguiente genera un resultado de Blog
& IEnumerable<Post>
. Dado que las bases de datos (especialmente las bases de datos relacionales) no tienen una manera de representar una colección de objetos del lado cliente, GroupJoin no se traduce en el servidor en muchos casos. Requiere que obtenga todos los datos del servidor para realizar GroupJoin sin un selector especial (primera consulta a continuación). Pero si el selector limita la selección de datos, la captura de todos los datos del servidor puede provocar problemas de rendimiento (segunda consulta a continuación). Por eso EF Core no traduce GroupJoin.
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
select new { b, grouping };
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
select new { b, Posts = grouping.Where(p => p.Content.Contains("EF")).ToList() };
SelectMany
El operador LINQ SelectMany permite enumerar a través de un selector de recopilación para cada elemento externo y generar tuplas de valores de cada origen de datos. De una manera, es una combinación pero sin ninguna condición, por lo que todos los elementos externos están conectados con un elemento del origen de la colección. En función de cómo esté relacionado el selector de recopilación con el origen de datos externo, SelectMany puede traducirse en varias consultas diferentes en el lado servidor.
El selector de colección no conecta con una referencia externa
Cuando el selector de recopilación no hace referencia a nada desde el origen externo, el resultado es un producto cartesiano de ambos orígenes de datos. Se traduce en CROSS JOIN
en bases de datos relacionales.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
CROSS JOIN [Posts] AS [p]
El selector de colección se refiere a un elemento externo en una cláusula where
Cuando el selector de recopilación tiene una cláusula where, que hace referencia al elemento externo, EF Core la traduce a una combinación de base de datos y usa el predicado como condición de combinación. Normalmente, este caso surge cuando se usa la navegación de colecciones en el elemento externo como selector de colecciones. Si la colección está vacía para un elemento externo, no se generaría ningún resultado para ese elemento externo. Pero si DefaultIfEmpty
se aplica en el selector de colección, el elemento externo se conectará con un valor predeterminado del elemento interno. Debido a esta distinción, este tipo de consultas se traduce a INNER JOIN
en ausencia de DefaultIfEmpty
y LEFT JOIN
cuando se aplica DefaultIfEmpty
.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId)
select new { b, p };
var query2 = from b in context.Set<Blog>()
from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
INNER JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
El selector de recopilación hace referencia al exterior en un caso distinto de where
Cuando el selector de recopilación hace referencia al elemento externo, que no está en una cláusula where (como en el caso anterior), no se traduce a una combinación de base de datos. Por eso es necesario evaluar el selector de recopilación para cada elemento externo. Se traduce a APPLY
operaciones en muchas bases de datos relacionales. Si la colección está vacía para un elemento externo, no se generaría ningún resultado para ese elemento externo. Pero si DefaultIfEmpty
se aplica en el selector de colección, el elemento externo se conectará con un valor predeterminado del elemento interno. Debido a esta distinción, este tipo de consultas se traduce a CROSS APPLY
en ausencia de DefaultIfEmpty
y OUTER APPLY
cuando se aplica DefaultIfEmpty
. Algunas bases de datos como SQLite no admiten APPLY
operadores, por lo que es posible que este tipo de consulta no se traduzca.
var query = from b in context.Set<Blog>()
from p in context.Set<Post>().Select(p => b.Url + "=>" + p.Title)
select new { b, p };
var query2 = from b in context.Set<Blog>()
from p in context.Set<Post>().Select(p => b.Url + "=>" + p.Title).DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], ([b].[Url] + N'=>') + [p].[Title] AS [p]
FROM [Blogs] AS [b]
CROSS APPLY [Posts] AS [p]
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], ([b].[Url] + N'=>') + [p].[Title] AS [p]
FROM [Blogs] AS [b]
OUTER APPLY [Posts] AS [p]
AgruparPor
Los operadores groupBy de LINQ crean un resultado de tipo IGrouping<TKey, TElement>
donde TKey
y TElement
podrían ser cualquier tipo arbitrario. Además, IGrouping
implementa IEnumerable<TElement>
, lo que significa que puede componer sobre él usando cualquier operador LINQ después de la agrupación. Dado que ninguna estructura de base de datos puede representar un IGrouping
, los operadores GroupBy no tienen traducción en la mayoría de los casos. Cuando se aplica un operador de agregado a cada grupo, que devuelve un escalar, se puede traducir a SQL GROUP BY
en bases de datos relacionales. Sql GROUP BY
también es restrictivo. Requiere que solo se agrupe por valores escalares. La proyección solo puede contener columnas de clave de agrupación o cualquier agregado aplicado sobre una columna. EF Core identifica este patrón y lo traduce en el servidor, como en el ejemplo siguiente:
var query = from p in context.Set<Post>()
group p by p.AuthorId
into g
select new { g.Key, Count = g.Count() };
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
EF Core también traduce las consultas en las que un operador de agregado de la agrupación aparece en un operador LINQ como Where u OrderBy (u otro operador de ordenación). Se utiliza HAVING
como parte de la cláusula WHERE en SQL. La parte de la consulta antes de aplicar el operador GroupBy puede ser cualquier consulta compleja siempre que se pueda traducir al servidor. Además, una vez que aplique operadores de agregado en una consulta de agrupación para quitar agrupaciones del origen resultante, puede componer una nueva consulta sobre ella como cualquier otra consulta.
var query = from p in context.Set<Post>()
group p by p.AuthorId
into g
where g.Count() > 0
orderby g.Key
select new { g.Key, Count = g.Count() };
SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
HAVING COUNT(*) > 0
ORDER BY [p].[AuthorId]
Los operadores agregados compatibles con EF Core son los siguientes:
.RED | SQL |
---|---|
Average(x => x.Property) | AVG(Property) |
Count() | COUNT(*) |
LongCount() | COUNT(*) |
Max(x => x.Property) | MAX(Propiedad) |
Min(x => x.Property) | MIN(Propiedad) |
Sum(x => x.Property) | SUM(Propiedad) |
Se pueden admitir operadores de agregado adicionales. Compruebe los documentos del proveedor para obtener más asignaciones de funciones.
Aunque no hay ninguna estructura de base de datos para representar IGrouping
, en algunos casos, EF Core 7.0 y versiones posteriores pueden crear las agrupaciones después de que se devuelvan los resultados de la base de datos. Esto es similar a cómo funciona el Include
operador al incluir colecciones relacionadas. La siguiente consulta LINQ usa el operador GroupBy para agrupar los resultados por el valor de su propiedad Price.
var query = context.Books.GroupBy(s => s.Price);
SELECT [b].[Price], [b].[Id], [b].[AuthorId]
FROM [Books] AS [b]
ORDER BY [b].[Price]
En este caso, el operador GroupBy no se traduce directamente en una GROUP BY
cláusula de SQL, sino que EF Core crea las agrupaciones después de que se devuelvan los resultados desde el servidor.
Unión a la izquierda
Aunque Left Join no es un operador LINQ, las bases de datos relacionales tienen el concepto de combinación izquierda que se usa con frecuencia en las consultas. Un patrón determinado en las consultas LINQ da el mismo resultado que un LEFT JOIN
en el servidor. EF Core identifica estos patrones y genera el equivalente LEFT JOIN
en el lado servidor. El patrón implica crear un GroupJoin entre los orígenes de datos y, a continuación, aplanar la agrupación mediante el operador SelectMany con DefaultIfEmpty en el origen de agrupación para que coincida con NULL cuando el elemento interno no tiene un elemento relacionado. En el ejemplo siguiente se muestra cómo es ese patrón y lo que genera.
var query = from b in context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
from p in grouping.DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
El patrón anterior crea una estructura compleja en el árbol de expresiones. Por esta razón, EF Core requiere que desagrupe los resultados del operador GroupJoin en el paso inmediatamente siguiente al operador. Incluso si se usa groupJoin-DefaultIfEmpty-SelectMany pero en un patrón diferente, es posible que no lo identifiquemos como unión izquierda.