Erstellen Sie ein Tupel in einer Linq-Auswahl

83

Ich arbeite mit C # und .NET Framework 4.5.1 und rufe mit Entity Framework 6.1.3 Daten aus einer SQL Server-Datenbank ab.

Ich habe das:

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

Und wenn ich es starte, bekomme ich folgende Nachricht:

In LINQ to Entities werden nur parameterlose Konstruktoren und Initialisierer unterstützt.

Ich weiß nicht, wie ich das Tupel erstellen muss, da alle Beispiele, die ich gefunden habe, größtenteils wie dieses sind.

Ich habe das versucht:

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

Und erhalten Sie diesen Fehler:

LINQ to Entities erkennt die Methode 'System.Tuple`2 [System.String, System.Byte] Create [String, Byte] (System.String, Byte)' nicht und diese Methode kann nicht in einen Speicherausdruck übersetzt werden.

Wo ist das Problem?

VansFannel
quelle
Sieht so aus, als müssten Sie ein stark typisiertes Objekt erstellen. Aber ja, gute Frage; upvoted.
Frenchie

Antworten:

111

Während die Antwort von octavioccl funktioniert, ist es besser, zuerst das Abfrageergebnis in einen anonymen Typ zu projizieren und dann zu enumerable zu wechseln und es in tuple zu konvertieren. Auf diese Weise ruft Ihre Abfrage nur die benötigten Felder aus der Datenbank ab.

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

Hinweis: Die obige Regel gilt für EF6. EF Core unterstützt natürlich Tupel (in Projektion oder als Join- / Gruppenschlüssel) über den Tupelkonstruktor, z. B. funktioniert die ursprüngliche Abfrage einfach

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

aber nicht die Tuple.CreateMethode (EF Core 2.x).

Ivan Stoev
quelle
Sehr gute Lösung - Danke! ... Was wäre, wenn ich diese Lösung um einen anderen nullbaren Wert erweitern müsste? .Select(c => new { c.Id, c.Flag, c.Foo?.Code })funktioniert nicht.
Skyfrog
2
@skyfrog Der Operator ?.wird in Ausdrucksbäumen nicht unterstützt. Ansonsten können Sie den anonymen Typ mit beliebig vielen Werten erweitern - vergessen Sie nicht, sie bei Bedarf zu benennen :) zBc => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
Ivan Stoev
1
Toll! Vielen Dank @Ivan für Ihre Antwort. Ich war so nah! ... aber es ist immer leicht zu sagen, wenn man zurückblickt ;-)
Skyfrog
Gute Antwort, kann mit EF-Entities verwendet werden .. zB dbCtx.MyEntity.Where (). Wählen Sie (.. zu einem Objekt ...).
Etc
40

Nur eine aktualisierte Antwort für C # 7, jetzt können Sie eine einfachere Syntax verwenden, um ValueTuples zu erstellen.

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

Sie können jetzt sogar die Eigenschaften des Tupels benennen:

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

Anstatt es als Item1 oder Item2 zu verwenden, können Sie als Id oder Flag darauf zugreifen.

Rafael Merlin
quelle
11

In linq to entity können Sie auf einen anonymen Typ oder auf ein DTO projizieren. Um dieses Problem zu vermeiden, können Sie die AsEnumerableErweiterungsmethode verwenden:

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

Mit dieser Methode können Sie mit der Arbeit Linq to Object statt Linq to Entities , so nach dem es nennen, können Sie das Ergebnis der Abfrage im Projekt , was Sie need.The Vorteil der Verwendung AsEnumerablestattdessen ToListwird , dass AsEnumerablesich die Abfrage nicht ausgeführt werden , es Ausführung latenten bewahrt. Es ist eine gute Idee, Ihre Daten immer zuerst zu filtern, bevor Sie eine dieser Methoden aufrufen.

octavioccl
quelle
10

Versuche dies:

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

Wurde informiert, dass dies in LINQ für Entitäten nicht akzeptiert wird.

Eine andere Möglichkeit wäre, das Ergebnis vor der Auswahl in den Speicher zu ziehen. Wenn Sie dies tun, würde ich empfehlen, die gesamte Filterung vor .AsEnumerable () durchzuführen, da dies bedeutet, dass Sie nur die gewünschten Ergebnisse zurückziehen, anstatt die gesamte Tabelle zurückzuziehen und dann zu filtern.

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

Außerdem kann Tuple.Create (c.Id, c.Flag) in neues Tuple (c.Id, c.Flag) geändert werden, wenn Sie den Code in den Tupeltypen etwas expliziter gestalten möchten

Dhunt
quelle
Entschuldigung, es funktioniert nicht. Ich habe meine Frage mit weiteren Details aktualisiert.
VansFannel
5

Ich habe die Antwort gefunden:

codes = codesRepo.SearchFor(predicate)
      .ToList()
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();
VansFannel
quelle
Nein, dies wird SELECT *
Mihai Bratulescu
1

Verwenden Sie dazu diese Methode und verwenden Sie den 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);
MohammadSoori
quelle
0

Nur meine zwei Cent: Das hat mich ein paar Mal mit den Typnamen erwischt:

Ein paar knifflige Beispiele:

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

Grüße.

IbrarMumtaz
quelle
Die von Ihnen veröffentlichte Syntax funktioniert nicht. Was Sie wahrscheinlich schreiben wollten, ist public (string Id, byte Flag) SearchFor(Expression predicate), aber das ist nebensächlich. Zwei Cent sollten keine Antwort sein, sondern ein Kommentar.
M. Stramm
1
Ich habe meine Antwort aktualisiert - ich hätte sie vor dem Posten überprüfen sollen. Ich bin nicht einverstanden; Alle Informationen sind für alle Besucher nützlich, die auf dieser Seite landen, unabhängig davon, wie sie positioniert sind. Kommentare vermitteln nicht die Absicht sowie die Antwort dank der Antworten.
IbrarMumtaz
Ich bin damit einverstanden, dass hinzugefügte Inhalte gut sind und Kommentare nicht gut für Codebeispiele geeignet sind. Vielen Dank für die Bearbeitung. Jetzt ist klar, dass dies keine Antwort auf die Frage des OP ist (aber bei Tupelproblemen hilfreich sein kann).
M. Stramm