Criar uma tupla em um Linq Select


87

Estou trabalhando com C # e .NET Framework 4.5.1 recuperando dados de um banco de dados SQL Server com Entity Framework 6.1.3.

Eu tenho isto:

codes = codesRepo.SearchFor(predicate)
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

E quando eu o executo, recebo esta mensagem:

Apenas construtores e inicializadores sem parâmetros têm suporte no LINQ to Entities.

Não sei como devo criar a Tupla, porque todos os exemplos que encontrei são em sua maioria como este.

Eu tentei isso:

codes = codesRepo.SearchFor(predicate)
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();

E obter este erro:

LINQ to Entities não reconhece o método 'System.Tuple`2 [System.String, System.Byte] Create [String, Byte] (System.String, Byte)' e este método não pode ser convertido em uma expressão de armazenamento.

Onde está o problema?


Parece que você precisará criar um objeto fortemente tipado. Mas sim, boa pergunta; votado.
— francês

Respostas:


118

Embora a resposta por octavioccl funcione, é melhor primeiro projetar o resultado da consulta no tipo anônimo e, em seguida, mudar para enumerável e convertê-lo em tupla. Desta forma, a sua consulta recuperará da base de dados apenas os campos necessários.

codes = codesRepo.SearchFor(predicate)
    .Select(c => new { c.Id, c.Flag })
    .AsEnumerable()
    .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
    .ToList();

Nota: A regra acima se aplica ao EF6. EF Core naturalmente suporta tuplas (em projeção ou como chaves de junção / grupo) via construtor de tupla, por exemplo, a consulta original simplesmente funciona

codes = codesRepo.SearchFor(predicate)
  .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
  .ToList();

mas não o Tuple.Createmétodo (EF Core 2.x).


Muito boa solução - Obrigado! ... E se eu tivesse que estender essa solução por outro valor anulável? .Select(c => new { c.Id, c.Flag, c.Foo?.Code })não funciona.
— skyfrog de

2
@skyfrog O operador ?.não é compatível com árvores de expressão. Mas além disso, você pode estender o tipo anônimo com quantos valores quiser - apenas não se esqueça de nomeá-los quando necessário :) por exemploc => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
— Ivan Stoev

1
Ótimo! Muito obrigado @Ivan por sua resposta. Eu estava tão perto! ... mas é sempre fácil dizer olhando para trás ;-)
— skyfrog

Ótima resposta, pode ser usado com EF-Entities .. por exemplo, dbCtx.MyEntity.Where (). Select (.. to anon object ...). Etc ...
— joedotnot

44

Apenas uma resposta atualizada para C # 7, agora você pode usar uma sintaxe mais simples para criar ValueTuples.

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag })
.AsEnumerable()
.Select(c => (c.Id, c.Flag))
.ToList();

Você pode até nomear as propriedades da tupla agora:

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag }) // anonymous type
.AsEnumerable()
.Select(c => (Id: c.Id, Flag: c.Flag)) // ValueTuple
.ToList();

Portanto, em vez de usá-lo como Item1 ou Item2, você pode acessá-lo como Id ou Flag.

Mais documentos sobre como escolher entre anônimo e tupla


11

Experimente isto:

codes = codesRepo.SearchFor(predicate)
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

Foi informado que isso não está aceitando no LINQ para entidades.

Outra opção seria puxar o resultado para a memória antes de selecionar. Se você vai fazer isso, eu recomendaria fazer toda a filtragem antes de .AsEnumerable (), pois isso significa que você está apenas obtendo os resultados desejados, em vez de retirar toda a tabela e, em seguida, filtrar.

codes = codesRepo.SearchFor(predicate).AsEnumerable()
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

também Tuple.Create (c.Id, c.Flag) pode ser alterado para novo Tuple (c.Id, c.Flag) se você quiser tornar o código um pouco mais explícito nos tipos de tuplas


Desculpe, não funciona. Eu atualizei minha pergunta com mais detalhes.
— VansFannel

11

Nas entidades linq to, você pode projetar em um tipo anônimo ou DTO. Para evitar esse problema, você pode usar o AsEnumerablemétodo de extensão:

codes = codesRepo.SearchFor(predicate).AsEnumerable().
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

Este método permite trabalhar com Linq to Object ao invés de Linq to Entities , então após chamá-lo, você pode projetar o resultado de sua consulta no que você precisar. A vantagem de usar em AsEnumerablevez disso ToListé que AsEnumerablenão executa a consulta, preserva a execução adiada. É uma boa ideia sempre filtrar seus dados antes de chamar um desses métodos.


5

Eu encontrei a resposta:

codes = codesRepo.SearchFor(predicate)
      .ToList()
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();

Não, isso gerará SELECT *
— Mihai Bratulescu

1

Use este método para fazer isso e use o assíncrono.

var codes = await codesRepo.SearchFor(predicate)
                    .Select(s => new
                    {
                        Id = s.Id,
                        Flag = s.Flag
                    }).FirstOrDefaultAsync();

                var return_Value = new Tuple<string, byte>(codes.Id, codes.Flag);

0

Apenas meus dois centavos: isso me surpreendeu algumas vezes com os nomes dos tipos:

Alguns exemplos complicados:

    private Tuple<string, byte> v1()
    {
        return new Tuple<string, byte>("", 1);
    }

    private (string, int) v2()
    {
        return ("", 1);
    }

    private (string Id, byte Flag) v3()
    {
        return ("", 1);
    }

Saudações.


A sintaxe que você postou não funciona. O que você provavelmente pretendia escrever é public (string Id, byte Flag) SearchFor(Expression predicate), mas isso não vem ao caso. Dois centavos não devem ser uma resposta, mas um comentário.
— M.Stramm de

2
Atualizei minha resposta - deveria ter verificado antes de postar. Discordo; todas as informações são úteis para todos os visitantes que chegam a esta página, independentemente de como sejam postadas. Os comentários não transmitem a intenção, bem como respondem, graças às respostas.
— IbrarMumtaz de

Eu concordo que o conteúdo adicionado é bom e os comentários não atendem bem aos exemplos de código. Obrigado pela edição, agora está claro que esta não é uma resposta à pergunta do OP (mas pode ajudar com problemas relacionados à tupla).
— M.Stramm
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.