Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Language Integrated Query (LINQ) contém muitos operadores complexos, que combinam várias fontes de dados ou fazem processamento complexo. Nem todos os operadores LINQ têm traduções adequadas no lado do servidor. Às vezes, uma consulta em um formulário se traduz para o servidor, mas se escrita em um formulário diferente não traduz, mesmo que o resultado seja o mesmo. Esta página descreve alguns dos operadores complexos e suas variações suportadas. Em versões futuras, podemos reconhecer mais padrões e adicionar suas traduções correspondentes. Também é importante ter em mente que o suporte à tradução varia entre os provedores. Uma consulta específica, que é traduzida em SqlServer, pode não funcionar para bancos de dados SQLite.
Sugestão
Você pode visualizar a amostra do deste artigo no GitHub.
Aderir
O operador LINQ Join permite conectar duas fontes de dados com base no seletor de chaves para cada fonte, gerando uma tupla de valores quando a chave corresponde. Traduz-se naturalmente para INNER JOIN
em bases de dados relacionais. Embora o LINQ Join tenha seletores de chaves externos e internos, o banco de dados requer uma única condição de junção. Assim, o EF Core gera uma condição de junção comparando o seletor de chave externo com o seletor de chave interno para igualdade.
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]
Além disso, se os seletores de chave forem tipos anônimos, o EF Core gerará uma condição de junção para comparar a igualdade em termos 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'))
Entrar no Grupo
O operador LINQ GroupJoin permite conectar duas fontes de dados semelhantes a Join, mas cria um grupo de valores internos para elementos externos correspondentes. A execução de uma consulta como o exemplo a seguir gera um resultado de Blog
& IEnumerable<Post>
. Como os bancos de dados (especialmente os bancos de dados relacionais) não têm uma maneira de representar uma coleção de objetos do lado do cliente, o GroupJoin não se traduz para o servidor em muitos casos. Ele requer que você obtenha todos os dados do servidor para fazer GroupJoin sem um seletor especial (primeira consulta abaixo). Mas se o seletor estiver limitando os dados que estão sendo selecionados, buscar todos os dados do servidor pode causar problemas de desempenho (segunda consulta abaixo). É por isso que o EF Core não traduz 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() };
Selecionar Vários
O operador LINQ SelectMany permite enumerar sobre um seletor de coleção para cada elemento externo e gerar tuplas de valores de cada fonte de dados. De certa forma, é uma junção, mas sem qualquer condição, de modo que cada elemento externo está conectado com um elemento da fonte de coleta. Dependendo de como o seletor de coleção está relacionado à fonte de dados externa, o SelectMany pode ser traduzido em diferentes consultas no lado do servidor.
O seletor de coleção não faz referência externa
Quando o seletor de coleção não está fazendo referência a nada da fonte externa, o resultado é um produto cartesiano de ambas as fontes de dados. É traduzido para CROSS JOIN
em bases de dados relacionais.
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]
Referências externas do seletor de coleção numa cláusula where
Quando o seletor de coleção tem uma cláusula where, que faz referência ao elemento externo, o EF Core a traduz para uma junção de banco de dados e usa o predicado como a condição de junção. Normalmente, este caso surge ao usar a navegação de coleção no elemento externo como seletor de coleção. Se a coleção estiver vazia para um elemento externo, nenhum resultado será gerado para esse elemento externo. Mas se DefaultIfEmpty
for aplicado no seletor de coleção, o elemento externo será conectado com um valor padrão do elemento interno. Devido a esta distinção, este tipo de consultas traduz-se para INNER JOIN
na ausência de DefaultIfEmpty
e LEFT JOIN
, quando DefaultIfEmpty
é aplicado.
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]
Referências externas do seletor de coleção em um caso não específico
Quando o seletor de coleção faz referência ao elemento externo, que não está em uma cláusula where (como o caso acima), ele não se traduz em uma junção de banco de dados. É por isso que precisamos avaliar o seletor de coleção para cada elemento externo. Isto traduz-se em APPLY
operações em muitos bancos de dados relacionais. Se a coleção estiver vazia para um elemento externo, nenhum resultado será gerado para esse elemento externo. Mas se DefaultIfEmpty
for aplicado no seletor de coleção, o elemento externo será conectado com um valor padrão do elemento interno. Devido a esta distinção, este tipo de consultas traduz-se para CROSS APPLY
na ausência de DefaultIfEmpty
e OUTER APPLY
, quando DefaultIfEmpty
é aplicado. Certos bancos de dados, como SQLite, não suportam APPLY
operadores, portanto, esse tipo de consulta pode não ser traduzido.
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
Os operadores LINQ GroupBy criam um resultado do tipo IGrouping<TKey, TElement>
onde TKey
e TElement
pode ser qualquer tipo arbitrário. Além disso, IGrouping
implementa IEnumerable<TElement>
, o que significa que você pode compor sobre ele usando qualquer operador LINQ após o agrupamento. Como nenhuma estrutura de banco de dados pode representar um IGrouping
, os operadores GroupBy não têm tradução na maioria dos casos. Quando um operador agregado é aplicado a cada grupo, que retorna um escalar, ele pode ser traduzido para SQL GROUP BY
em bancos de dados relacionais. O SQL GROUP BY
também é restritivo. Ele requer que você agrupe apenas por valores escalares. A projeção só pode conter colunas de chave de agrupamento ou qualquer agregação aplicada sobre uma coluna. O EF Core identifica esse padrão e o traduz para o servidor, como no exemplo a seguir:
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]
O EF Core também traduz consultas em que um operador agregado no agrupamento aparece num operador LINQ Where ou OrderBy (ou outro operador de ordenação). Usa a cláusula HAVING
em SQL para a cláusula WHERE. A parte da consulta antes de aplicar o operador GroupBy pode ser qualquer consulta complexa, desde que possa ser traduzida para o servidor. Além disso, depois de aplicar operadores agregados em uma consulta de agrupamento para remover agrupamentos da fonte resultante, você pode compor sobre ela como qualquer outra 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]
Os operadores agregados que o EF Core suporta são os seguintes:
.NET | SQL |
---|---|
Média(x = > x.Propriedade) | AVG(Propriedade) |
Contagem() | CONTAGEM(*) |
Contagem longa() | CONTAGEM(*) |
Max(x => x.Propriedade) | MAX(Propriedade) |
Mínimo(x => x.Propriedade) | MIN (Propriedade) |
Soma(x => x.Propriedade) | SOMA(Propriedade) |
Operadores agregados adicionais podem ser suportados. Verifique os documentos do seu provedor para obter mais mapeamentos de funções.
Embora não haja uma estrutura de banco de dados para representar um IGrouping
, em alguns casos, o EF Core 7.0 e versões mais recentes podem criar os agrupamentos depois que os resultados são retornados do banco de dados. Isto é semelhante à forma como o Include
operador trabalha ao incluir coleções relacionadas. A consulta LINQ a seguir usa o operador GroupBy para agrupar os resultados pelo valor de sua propriedade Price.
var query = context.Books.GroupBy(s => s.Price);
SELECT [b].[Price], [b].[Id], [b].[AuthorId]
FROM [Books] AS [b]
ORDER BY [b].[Price]
Nesse caso, o operador GroupBy não se traduz diretamente para uma GROUP BY
cláusula no SQL, mas em vez disso, o EF Core cria os agrupamentos depois que os resultados são retornados do servidor.
Junção à esquerda
Embora o Left Join não seja um operador LINQ, os bancos de dados relacionais têm o conceito de Left Join que é frequentemente usado em consultas. Um padrão específico em consultas LINQ dá o mesmo resultado que um LEFT JOIN
no servidor. O EF Core identifica esses padrões e gera o equivalente LEFT JOIN
no lado do servidor. O padrão envolve a criação de um GroupJoin entre ambas as fontes de dados e, em seguida, aplanar o agrupamento usando o operador SelectMany com DefaultIfEmpty na fonte de agrupamento para corresponder a null quando a fonte interna não possui um elemento relacionado. O exemplo a seguir mostra como esse padrão se parece e o que ele gera.
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]
O padrão acima cria uma estrutura complexa na árvore de expressão. Por isso, o EF Core exige que você nivele os resultados de agrupamento do operador GroupJoin em uma etapa imediatamente após o operador. Mesmo que o GroupJoin-DefaultIfEmpty-SelectMany seja usado, mas em um padrão diferente, podemos não identificá-lo como um Left Join.