2012-04-19 18 views
9

Ist es möglich, eine Gruppe nach einem Objekt durchzuführen?Entity Framework GroupBy ein Objekt oder ComplexType

from item in context.Items 
group item by item.MyObject 
select ... 

Wo Item.MyObject ein einfaches Objekt ist, wie:

public class MyObject { 
    public int SomeValue { get; set; } 
    public string SomeName { get; set; } 
    public string SomeOtherProperty { get; set; } 
} 

Offensichtlich konnte ich folgendes tun: Doch

from item in context.Items 
group item by new { item.SomeValue, item.SomeName, item.SomeOtherProperty } 
select ... 

, wenn sie von Objekten mit vielen Eigenschaften Gruppierung, Dieser Ansatz ist mühsam und fehleranfällig.

Der obige Code führt zu einer NotSupportedException mit der folgenden Meldung: "Der Schlüsselauswahltyp für den Aufruf der 'GroupBy' -Methode ist im zugrunde liegenden Speicheranbieter nicht vergleichbar". Das Überschreiben von Equals und GetHashcode hat keine Auswirkungen. Ich vermute das wahre Problem ist, dass das Entity-Framework nicht weiß, wie man SQL ausdrückt ...?

+0

Ist 'MyObject' Teil des EF-Modells? Ich kann eine ähnliche Sache mit einer Navigationseigenschaft auf 'MyObject' machen (wo MyObject: Item ist n: 1). Das generierte SQL ist schrecklich, aber zumindest funktioniert es. –

+0

Es ist eigentlich ein ComplexType. Kurz gesagt, ja, EF ist sich des Objekttyps bewusst. Da alle Eigenschaften tatsächlich in der gleichen Tabelle vorhanden sind, würde ich erwarten, dass das resultierende SQL viel sauberer ist, als wenn es eine Navigationseigenschaft verwenden würde (wenn ich es natürlich arbeiten könnte!) – mindlessgoods

Antwort

7

Mitleid, nein, das geht nicht. Der ComplexType sieht vielversprechend aus und scheint eine nette "Gruppe von Spalten" zu sein - aber das EF-Team hat es einfach für eine komplett andere Nutzung entworfen und viele Funktionen, die mit der Verwendung als "Gruppe von Spalten" zusammenhängen, fehlen einfach.

Soweit mein Wissen reicht, ist das "Offensichtlich kann ich Folgendes tun:" der einzige mögliche Weg. Sie müssen die Gruppierung entweder auf einer Unterspaltenbasis oder durch eine Projektion auf einen Typ definieren, den die Datenbank vergleichen kann.

Das Kernproblem ist, dass das EF-Team die Complex sieht nicht als eine Gruppe von Spalten, mit denen Sie Ihre Einheiten mit vielen Feldern aufzuräumen, aber für sie es eine andere Art von Person ist, Art eine transiente. Das ComplexType-Objekt soll Objekte darstellen, die beispielsweise aus einer gespeicherten Prozedur (oder Tabellen zurückgebenden Funktionen) zurückgegeben werden, die eine Ergebnismenge von 5 Spalten zurückgibt, die keiner normalen vorhandenen Tabelle/Klasse zugeordnet werden können. Stellen Sie sich zum Beispiel vor, Sie hätten eine Tabelle/Klasse Customer, die 50 Spalten/Felder enthält, von denen die meisten nicht null sind und einige sensible Daten darstellen. Sie erstellen eine gespeicherte Prozedur, die eine ID des Kunden annimmt und {Name, Adresse, Kontakttelefon, Schuhgröße} zurückgibt. Offensichtlich können solche beschnittenen Ergebnisse nicht festgelegt werden, und Sie können sie nicht in die Customer-Klasse dekodieren/abbilden, die über Tonnen von Feldern verfügt, die nicht null sein können. Wie stellen Sie das Ergebnis in EF dar?

Die erste Option, ziemlich lahm, ist einfach EF zu ignorieren und sprechen Sie direkt mit der Datenbank und lesen/übersetzen/entpacken Sie die Zeilen/Spalten von Hand. Nun, wir kennen SqlClient, das können wir machen. Die zweite Option, hässlich, besteht darin, in EF eine Stub-Tabelle/View zu definieren, beispielsweise CustomerView, die genau die Spalten {name, address, contact phone, shoe size} enthält und die Ergebnismenge der gespeicherten Prozedur abbildet zu ihm. Es würde gut funktionieren, aber wenn Sie durch Zufall "Datenbank erzeugen" das EF-Modell bilden, erhalten Sie diese extra unbenutzte Tabelle auch.

Die dritte Option zur Rettung ist der ComplexType. Anstatt zu versuchen, eine Schattentabelle oder -ansicht zu erstellen, teilen Sie dem EF nur mit, dass "es einen zusätzlichen Datentyp gibt", dass niemals einer eigenen Tabelle zugeordnet wird und niemals einen PrimaryKey definiert, und die EF wird Zu Ihrer Bequemlichkeit ordnen Sie es wie jedes andere Datenobjekt einer Klasse zu. Wenn Sie einen solchen Typ haben, können Sie ihn aus Prozeduren zurückgeben und ihn als ein gut typisiertes Objekt abrufen. Sie können ihn als Prozedurparameter usw. übergeben. Aber: Sie können ihn nicht alleine beibehalten. Es hat keine eigene Tabellenzuordnung und keine eigenen Primärschlüssel.Es hat keine Identität.

Es ist großartig, aber gut, warum wollten Sie diesen Datentyp von einer Prozedur zurückgegeben haben? Wahrscheinlich, die Prozedur verarbeitet oder erzeugt einige Daten, und Sie möchten speichern Sie sie in einer Tabelle. Sie haben keine ganz normale Entität definiert, daher handelt es sich bei dieser Sache nur um ein Datenpaket. Daher werden Sie diesen Ergebniswert wahrscheinlich zusammen mit anderen Daten in diese Tabelle schreiben. Aus diesem Grund können Sie in der EF den ComplexType auf einige Spalten einer Tabelle abbilden: Wenn Sie dieses temporäre Ergebnisobjekt aus einer Prozedur erhalten haben und es nun irgendwo schreiben möchten, müssen Sie dies nicht spaltenweise manuell tun , ordnen Sie es nur den Spalten "Name, Adresse, Kontakttelefon, Schuhgröße" des Kunden zu.

Der wichtigste Punkt ist, dass der Datenbankserver niemals weiß, dass ein solcher ComplexType existiert. Wie gesagt, es hatte keine Identität. Wenn Sie versuchen, eine LINQ-Abfrage zu tun, wie

aset.Where(item => item.complexProperty == complexValue) 

dann ist das komplexerWert ein CLR-Objekt, das auf keine Tabelle zugeordnet ist, hat keine Identität, also NO PK, so EF hat völlig keine Ahnung, wie zu prüfen, ob Das "linke Objekt" ist das gleiche wie das "rechte Objekt". Hätte das Objekt eine Identität definiert, könnte es PK auf der Serverseite überprüfen oder einen Objektvergleich auf der Clientseite durchführen, aber hier - es scheitert einfach.

Ich stimme völlig zu, dass dies ein weiteres verdammt nützliches Feature in EF ist, das fehlt. Ich glaube, es wäre für das EF-Team sehr einfach, die ComplexTypes spaltenweise zu vergleichen, aber das taten sie nicht. Sie dachten einfach nicht, dass ComplexTypes zum Aufräumen der Tabellen verwendet werden kann. Sie haben gemeint, dass es ein transientes Datenpaket ist, das zu und von gespeicherten Prozeduren und Funktionen weitergeleitet wird. Schade.

+0

eine Randnotiz: Ich sage über den Vergleich , weil ich erst kürzlich darüber gestolpert bin, als ich versucht habe, über einen komplexen Typ zu filtern. Die Gruppierung ist sehr ähnlich, alles läuft auf ein Problem hinaus, das man nicht vergleichen kann. Wenn jemand Workarounds oder EF-Erweiterungen kennt, wäre ich mehr als glücklich darüber zu wissen. Im Moment habe ich nur Artikel gefunden, die sagten: "Tut mir leid, das ist noch nicht verfügbar, vielleicht in EF5" – quetzalcoatl

+0

Danke für die ausführliche Antwort - es ist ein weiteres Feature, das in zukünftigen Versionen gut aussehen würde. – mindlessgoods

Verwandte Themen