O LINQ To Entities não reconhece o método Last. Realmente?


144

Nesta consulta:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters
        .OrderBy(p => p.ServerStatus.ServerDateTime)
        .GroupBy(p => p.RawName)
        .Select(p => p.Last());
}

Eu tive que mudar isso para isso funcionar

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters
        .OrderByDescending(p => p.ServerStatus.ServerDateTime)
        .GroupBy(p => p.RawName)
        .Select(p => p.FirstOrDefault());
}

Eu não conseguia nem usar p.First(), para espelhar a primeira consulta.

Por que existem limitações básicas no que é de outra forma um sistema ORM tão robusto?


armazene seu objeto IEnumrable em uma nova variável e retorne variable.last (). vai funcionar.
Ali.Rashidi

Respostas:


220

Essa limitação se resume ao fato de que, eventualmente, ela deve converter essa consulta para SQL e o SQL possui um SELECT TOP(em T-SQL), mas não um SELECT BOTTOM(não existe).

Existe uma maneira fácil de contornar isso, basta ordenar a descida e depois fazer a First(), que foi o que você fez.

EDIT: Outros provedores possivelmente terão implementações diferentes de SELECT TOP 1, no Oracle provavelmente seria algo mais parecidoWHERE ROWNUM = 1

EDITAR:

Outra alternativa menos eficiente - eu não recomendo isso! - é chamar .ToList()seus dados antes .Last(), o que executará imediatamente a expressão LINQ To Entities que foi criada até esse ponto e, em seguida, o seu .Last () funcionará, porque nesse momento ele .Last()é efetivamente executado no contexto de um LINQ to Expressão de Objetos . (E como você apontou, isso pode trazer de volta milhares de registros e desperdiçar cargas de objetos de materialização da CPU que nunca serão usados)

Novamente, eu não recomendaria fazer isso segundo, mas ajuda a ilustrar a diferença entre onde e quando a expressão LINQ é executada.


e como o LINQ To SQL lida com esse cenário?
bevacqua 03/09/11

@Neil sim, eu sei que eu posso chamar ToList, mas eu prefiro não recuperar milhares de registros do banco de dados apenas para filtrá-los para baixo a cinco registros
Bevacqua

2
Se você sabe que sua consulta retornará pequenos resultados, a chamada ToListnão é tão ruim.
Justin Skiles

35

Em vez de Last(), tente o seguinte:

model.OrderByDescending(o => o.Id).FirstOrDefault();

14

Substitua Last()por um seletor LinqOrderByDescending(x => x.ID).Take(1).Single()

Algo assim seria bom se você preferir fazê-lo no Linq:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}

1
Existe algum motivo para usar .Take (1) .Single () em vez de .FirstOrDefault ()?
Tot Zam

2
@TotZam A substituição válida seria .First () nesse caso, já que Single () lança uma exceção se a contagem de itens não for exatamente 1. #
MEMark

0

Outra maneira de obter o último elemento sem OrderByDescending e carregar todas as entidades:

dbSet
    .Where(f => f.Id == dbSet.Max(f2 => f2.Id))
    .FirstOrDefault();

0

Isso ocorre porque o LINQ to Entities (e bancos de dados em geral) não oferece suporte a todos os métodos LINQ (consulte aqui para obter detalhes: http://msdn.microsoft.com/en-us/library/bb738550.aspx )

O que você precisa aqui é solicitar seus dados de forma que o "último" registro se torne "primeiro" e, em seguida, você possa usar o FirstOrDefault. Observe que o banco de dados geralmente não possui conceitos como "primeiro" e "último", não é como se o registro inserido mais recentemente fosse "último" na tabela.

Este método pode resolver seu problema

db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();

-2

Adicionar uma única função AsEnumerable()antes da função Selecionar funcionou para mim.
Exemplo:

return context.ServerOnlineCharacters
    .OrderByDescending(p => p.ServerStatus.ServerDateTime)
    .GroupBy(p => p.RawName).AsEnumerable()
    .Select(p => p.FirstOrDefault());

Ref: https://www.codeproject.com/Questions/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys


É recomendável incorporar o código de trabalho do link na sua resposta. As respostas somente para links atrairão atenção negativa. Forneça uma resposta completa adicionando o código encontrado para ajudar e resolver o problema. Isso resolve um problema de links que não funcionam devido a erros 404 no futuro.
Studocwho

1
adicionou o exemplo à minha resposta
Artem Levitin

O lado negativo desta resposta é que ela trará todos os resultados anteriores ao lado do servidor "AsEnumerable" e, em seguida, selecione o primeiro. Isso pode ser muito indesejável. (Eu tive uma situação como essa em que os resultados demoravam mais de 20 segundos devido aos registros de mais de 20k sendo trazidos para o servidor, depois que eu o movia de volta para o banco de dados, os resultados retornavam em menos de um segundo)
TChadwick
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.