Utwórz krotkę w Linq Select

89

Pracuję z C # i .NET Framework 4.5.1, pobierając dane z bazy danych SQL Server z Entity Framework 6.1.3.

Mam to:

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

Kiedy go uruchamiam, otrzymuję następujący komunikat:

LINQ to Entities obsługuje tylko konstruktory i inicjatory bez parametrów.

Nie wiem, jak mam stworzyć krotkę, ponieważ wszystkie przykłady, które znalazłem, są w większości takie jak ten.

Próbowałem tego:

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

I otrzymaj ten błąd:

LINQ to Entities nie rozpoznaje metody „System.Tuple” 2 [System.String, System.Byte] Create [String, Byte] (System.String, Byte) ”i tej metody nie można przetłumaczyć na wyrażenie magazynu.

Gdzie jest problem?

VansFannel
źródło
Wygląda na to, że musisz utworzyć obiekt o jednoznacznie określonym typie. Ale tak, dobre pytanie; głosowano.
frenchie

Odpowiedzi:

121

Podczas gdy odpowiedź przez octavioccl prac, to lepiej do pierwszego projektu wynik zapytania do anonimowego typu, a następnie przełączyć się na przeliczalne i przekształcić ją w krotce. W ten sposób twoje zapytanie pobierze z bazy danych tylko potrzebne pola.

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

Uwaga: powyższa zasada dotyczy EF6. EF Core w naturalny sposób obsługuje krotki (w projekcji lub jako klucze łączenia / grup) za pośrednictwem konstruktora krotek, np. Oryginalne zapytanie po prostu działa

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

ale nie Tuple.Createmetoda (EF Core 2.x).

Ivan Stoev
źródło
Bardzo dobre rozwiązanie - dzięki! ... A co jeśli musiałbym rozszerzyć to rozwiązanie o inną wartość zerową? .Select(c => new { c.Id, c.Flag, c.Foo?.Code })nie działa.
skyfrog
2
@skyfrog Operator ?.nie jest obsługiwany w drzewach wyrażeń. Ale poza tym możesz rozszerzyć typ anonimowy o dowolną liczbę wartości - po prostu nie zapomnij ich nazwać w razie potrzeby :) np.c => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
Ivan Stoev
1
Świetny! Wielkie dzięki @Ivan za twoją odpowiedź. Byłem tak blisko! ... ale patrząc wstecz zawsze łatwo powiedzieć ;-)
skyfrog
Świetna odpowiedź, może być używana z EF-Entities .. np. DbCtx.MyEntity.Where (). Select (.. to anon object ...). Etc ...
joedotnot
50

Tylko zaktualizowana odpowiedź dla języka C # 7, teraz możesz użyć prostszej składni do tworzenia ValueTuples.

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

Możesz teraz nawet nazwać właściwości krotki:

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

Więc zamiast używać go jako Item1 lub Item2, możesz uzyskać do niego dostęp jako Id lub Flag.

Więcej dokumentów na temat wyboru między anonimowymi a krotkami

Rafael Merlin
źródło
11

Spróbuj tego:

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

Zostałem poinformowany, że to nie jest akceptowane w LINQ to entity.

Inną opcją byłoby pobranie wyniku do pamięci przed wybraniem. Jeśli masz zamiar to zrobić, zalecałbym wykonanie całego filtrowania przed .AsEnumerable (), ponieważ oznacza to, że wycofujesz tylko wyniki, które chcesz, w przeciwieństwie do wycofywania całej tabeli, a następnie filtrowania.

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

również Tuple.Create (c.Id, c.Flag) można zmienić na nowy Tuple (c.Id, c.Flag), jeśli chcesz, aby kod był nieco bardziej wyraźny w typach krotek

Dhunt
źródło
Przepraszamy, to nie działa. Zaktualizowałem moje pytanie o więcej szczegółów.
VansFannel
10

W linq do encji możesz rzutować na anonimowy typ lub na DTO. Aby uniknąć tego problemu, możesz użyć AsEnumerablemetody rozszerzenia:

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

Ta metoda umożliwia pracę z Linq to Object zamiast Linq to Entities , więc po wywołaniu jej możesz rzutować wynik zapytania w dowolne miejsce. Zaletą użycia AsEnumerablezamiast tego ToListjest to, że AsEnumerablenie wykonuje zapytania, zachowuje odroczone wykonanie. Dobrym pomysłem jest zawsze filtrowanie danych przed wywołaniem jednej z tych metod.

octavioccl
źródło
-1 To nie robi tego samego, o co prosił PO. Czasami możliwość tworzenia krotek w zapytaniu do celów łączenia jest ważna.
Aron
5

Znalazłem odpowiedź:

codes = codesRepo.SearchFor(predicate)
      .ToList()
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();
VansFannel
źródło
Nie, to wygeneruje SELECT *
Mihai Bratulescu
1

Użyj tej metody, aby to zrobić, i użyj asynchronicznego.

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);
MohammadSoori
źródło
0

Tylko moje dwa centy: kilka razy mnie to złapało z nazwami typów:

Kilka przykładów Noddy:

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

Pozdrowienia.

IbrarMumtaz
źródło
Opublikowana składnia nie działa. Prawdopodobnie miałeś zamiar napisać public (string Id, byte Flag) SearchFor(Expression predicate), ale to nie ma znaczenia. Dwa centy nie powinny być odpowiedzią, ale komentarzem.
M.Stramm
2
Zaktualizowałem odpowiedź - przed wysłaniem powinienem ją sprawdzić. Nie zgadzam się; wszystkie informacje są przydatne dla wszystkich odwiedzających, którzy trafiają na tę stronę, niezależnie od jej położenia. Komentarze nie wyrażają intencji, a także odpowiedzi dzięki odpowiedziom.
IbrarMumtaz
Zgadzam się, że dodane treści są dobre, a komentarze nie są dobrze dopasowane do przykładów kodu. Dzięki za edycję, teraz jest jasne, że to nie jest odpowiedź na pytanie OP (ale może pomóc w problemach związanych z krotką).
M.Stramm