webentwicklung-frage-antwort-db.com.de

Konvertieren Sie anonymen Typ in neuen C # 7-Tupeltyp

Die neue Version von C # ist da, mit der nützlichen neuen Funktion Tuple Types:

public IQueryable<T> Query<T>();

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => new {
            id = o.Id,
            name = o.Name,
        })
        .First();

    return (id: obj.id, name: obj.name);
}

Gibt es eine Möglichkeit, mein anonymes Typobjekt obj in das Tupel zu konvertieren, das ich zurückgeben möchte, ohne Eigenschaft für Eigenschaft zuzuordnen (vorausgesetzt, die Namen der Eigenschaften stimmen überein)?

Der Kontext befindet sich in einem ORM, mein SomeType-Objekt verfügt über viele andere Eigenschaften und ist einer Tabelle mit vielen Spalten zugeordnet. Ich möchte eine Abfrage durchführen, die nur ID und NAME enthält. Daher muss der anonyme Typ in ein Tupel konvertiert werden, oder ein ORM Linq-Anbieter muss wissen, wie man ein Tupel versteht, und die mit den Eigenschaften verbundenen Spalten in die SQL-Select-Klausel einfügen.

29
lmcarreiro

Die kurze Antwort lautet: Nein, in der aktuellen Form von C # 7 gibt es keine rahmengebundene Möglichkeit, Ihre Ziele wörtlich zu erreichen, da Sie Folgendes erreichen möchten:

  • Linq-to-Entities
  • Zuordnung zu einer Teilmenge von Spalten
  • Vermeiden der Zuordnung von Eigenschaften von einem benutzerdefinierten oder anonymen Typ zu einem C # 7-Tupel durch direkte Zuordnung zu einem C # 7-Tupel.

Da Query<SomeType> Ein IQueryable verfügbar macht, muss jede Art von Projektion auf einen Ausdrucksbaum .Select(x => new {}) erfolgen.

Es gibt ein offenes Roslyn-Problem , um diese Unterstützung hinzuzufügen, aber es existiert noch nicht.

Daher können Sie, bis diese Unterstützung hinzugefügt wird, entweder manuell einen anonymen Typ einem Tupel zuordnen oder den gesamten Datensatz zurückgeben und das Ergebnis direkt einem Tupel zuordnen, um zwei Zuordnungen zu vermeiden. Dies ist jedoch offensichtlich ineffizient.


Während diese Einschränkung derzeit aufgrund mangelnder Unterstützung und der Unfähigkeit, parametrisierte Konstruktoren in einer .Select() -Projektion zu verwenden, in Linq-to-NHibernate und Linq-to-Sql gebacken wird Ein Hack in Form der Erstellung eines neuen System.Tuple in der .Select() -Projektion und anschließender Rückgabe eines ValueTuple mit der . ToValueTuple () -Erweiterungsmethode:

public IQueryable<T> Query<T>();

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => new System.Tuple<int, string>(o.Id, o.Name))
        .First();

    return obj.ToValueTuple();
}

Da System.Tuple einem Ausdruck zugeordnet werden kann, können Sie eine Teilmenge der Daten aus Ihrer Tabelle zurückgeben und dem Framework die Zuordnung zu Ihrem C # 7-Tupel ermöglichen. Anschließend können Sie die Argumente mit einer beliebigen Namenskonvention dekonstruieren:

(int id, string customName) info = GetSomeInfo();
Console.Write(info.customName);
18
David L

Natürlich, indem Sie das Tupel aus Ihrem LINQ-Ausdruck erstellen:

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => (o.Id,o.Name))
        .First();

    return obj;
}

Gemäß einer anderen Antwort in Bezug auf Tupel vor C # 7 können Sie AsEnumerable() verwenden, um zu verhindern, dass EF die Dinge durcheinander bringt. (Ich habe nicht viel Erfahrung mit EF, aber das sollte reichen :)

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .AsEnumerable()
        .Select(o => (o.Id,o.Name))
        .First();

    return obj;
}
17
Patrick Hofman

Tupel-Literale werden derzeit in Ausdrucksbäumen nicht unterstützt. Dies bedeutet jedoch nicht, dass der Typ ValueTuple nicht unterstützt wird. Erstelle es einfach explizit.

public (int id, string name) GetSomeInfo() =>
    Query<SomeType>()
        .Select(o => ValueTuple.Create(o.Id, o.Name))
        .First();
5
Jeff Mercado

Hinweis für alle Benutzer einer niedrigeren .NET-Version: Wenn Sie eine niedrigere .NET-Version als 4.7.2 oder .NET Core verwenden, sollten Sie System.ValueTuple mit Nuget Package Manager in Ihrem Projekt installieren.

Im Folgenden finden Sie ein Beispiel für das Abrufen eines Tupels von einer Linq to SQL-Abfrage:

var myListOfTuples = (from record1 in myTable.Query()
                     join record2 in myTable2.Query() on record1.Id = record2.someForeignKey
                     select new {record1, record2}).AsEnumerable()
                     .select(o => (o.record1, o.record2))
                     .ToList()

Das lief für mich aber nach dem Einchecken bekam ich einen Buildfehler ... lesen Sie weiter.

Für noch mehr Spaß und Spiel hatte ich leider aus irgendeinem Grund eine frühere Version von C # auf meinem Build-Server. Ich musste also zurückgehen, da das neue Tupel-Format in der Zeile .select (o => (o.record1, o.record2)) nicht erkannt wurde (insbesondere, dass es sich aufgrund der Klammern um o um ein Tupel handeln würde. record1 und o.record2). Also musste ich zurückgehen und es ein bisschen genauer untersuchen:

var myListOfAnonymousObjects = (from record1 in myTable.Query()
                     join record2 in myTable2.Query() on record1.Id = record2.someForeignKey
                     select new {record1, record2}).ToList()

var myTuples = new List<Tuple<Record1sClass, Record2sClass>>();
foreach (var pair in myListOfAnonymousObjects)
{
    myTuples.Add(pair.record1, pair.record2);
}
return myTuples;
1
JakeJ
    public IList<(int DictionaryId, string CompanyId)> All(string DictionaryType)
    {
        var result = testEntities.dictionary.Where(p => p.Catalog == DictionaryType).ToList();
        var resultTuple = result.Select(p => (DictionaryId: p.ID, CompanyId: p.CompanyId));
        return resultTuple.ToList();
    }

Mit dieser Methode können Sie Ihr Tupel-Element in einer Linq-Auswahl benennen

0
Jackdon Wang