Creare una tupla in una selezione Linq


89

Sto lavorando con C # e .NET Framework 4.5.1 recuperando dati da un database SQL Server con Entity Framework 6.1.3.

Ho questo:

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

E quando lo eseguo, ricevo questo messaggio:

Solo i costruttori e gli inizializzatori senza parametri sono supportati in LINQ to Entities.

Non so come devo creare la tupla perché tutti gli esempi che ho trovato sono per lo più come questo.

Ho provato questo:

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

E ottieni questo errore:

LINQ to Entities non riconosce il metodo 'System.Tuple`2 [System.String, System.Byte] Create [String, Byte] (System.String, Byte)' e questo metodo non può essere tradotto in un'espressione di archivio.

Dov'è il problema?


Sembra che dovrai creare un oggetto fortemente tipizzato. Ma sì, bella domanda; votato.
Frenchie

Risposte:


121

Sebbene la risposta di octavioccl funzioni , è meglio proiettare prima il risultato della query in tipo anonimo, quindi passare a enumerabile e convertirlo in tupla. In questo modo la tua query recupererà dal database solo i campi necessari.

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

Nota: la regola precedente si applica a EF6. EF Core supporta naturalmente le tuple (in proiezione o come chiavi di join / gruppo) tramite il costruttore di tuple, ad esempio la query originale funziona semplicemente

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

ma non il Tuple.Createmetodo (EF Core 2.x).


Ottima soluzione - Grazie! ... E se dovessi estendere questa soluzione con un altro valore nullable? .Select(c => new { c.Id, c.Flag, c.Foo?.Code })non funziona.
skyfrog

2
@skyfrog L'operatore ?.non è supportato negli alberi delle espressioni. Ma a parte questo, puoi estendere il tipo anonimo con tutti i valori che desideri: non dimenticare di nominarli quando necessario :) ad es.c => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
Ivan Stoev

1
Grande! Molte grazie @Ivan per la tua risposta. Ero così vicino! ... ma è sempre facile da dire guardando indietro ;-)
skyfrog

Ottima risposta, può essere utilizzata con EF-Entities .. ad esempio dbCtx.MyEntity.Where (). Seleziona (.. to anon object ...). Etc ...
joedotnot

50

Solo una risposta aggiornata per C # 7, ora puoi usare una sintassi più semplice per creare ValueTuples.

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

Puoi anche nominare le proprietà della tupla ora:

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

Quindi, invece di usarlo come Item1 o Item2, puoi accedervi come Id o Flag.

Altri documenti sulla scelta tra anonimo e tupla


11

Prova questo:

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

È stato informato che questo non è accettato in LINQ to entity.

Un'altra opzione sarebbe quella di inserire il risultato in memoria prima di selezionarlo. Se hai intenzione di farlo, ti consiglio di fare tutto il filtraggio prima di .AsEnumerable () in quanto significa che stai solo ritirando i risultati desiderati invece di tirare indietro l'intera tabella e quindi filtrare.

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

anche Tuple.Create (c.Id, c.Flag) potrebbe essere cambiato in new Tuple (c.Id, c.Flag) se vuoi rendere il codice un po 'più esplicito nei tipi di tuple


Spiacenti, non funziona. Ho aggiornato la mia domanda con maggiori dettagli.
VansFannel

10

In linq to entity puoi proiettare su un tipo anonimo o su un DTO.Per evitare questo problema puoi usare il AsEnumerablemetodo di estensione:

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

Questo metodo ti consente di lavorare con Linq to Object invece di Linq to Entities , quindi dopo averlo chiamato, puoi proiettare il risultato della tua query in qualsiasi cosa tu abbia bisogno.Il vantaggio di usare AsEnumerableinvece ToListè che AsEnumerablenon esegue la query, preserva l'esecuzione differita. È una buona idea filtrare sempre i dati prima di chiamare uno di questi metodi.


-1 Questo non fa la stessa cosa che è stata richiesta dall'OP. A volte, essere in grado di creare tuple in una query, per scopi di unione è importante.
Aron

5

Ho trovato la risposta:

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

No, questo genererà SELECT *
Mihai Bratulescu

1

Usa questo metodo per farlo e usa il metodo async.

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

Solo i miei due centesimi: questo mi ha colto un paio di volte con i nomi dei tipi:

Alcuni esempi assurdi:

    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);
    }

Saluti.


La sintassi che hai pubblicato non funziona. Quello che probabilmente intendevi scrivere è public (string Id, byte Flag) SearchFor(Expression predicate), ma non è questo il punto. Due centesimi non dovrebbero essere una risposta, ma un commento.
M.Stramm

2
Ho aggiornato la mia risposta - avrei dovuto controllarla prima di pubblicare. Non sono d'accordo; tutte le informazioni sono utili a tutti i visitatori che atterrano su questa pagina indipendentemente da come viene posta. I commenti non esprimono l'intenzione così come la risposta grazie alle risposte.
IbrarMumtaz

Sono d'accordo che il contenuto aggiunto è buono e che i commenti non si adattano bene agli esempi di codice. Grazie per l'editing, ora è chiaro che questa non è una risposta alla domanda dell'OP (ma può aiutare con i problemi relativi alle tuple).
M.Stramm
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.