2012-04-19 14 views
9

È possibile eseguire un gruppo in base a un oggetto?Entity Framework GroupBy un oggetto o ComplexType

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

Dove Item.MyObject è un semplice oggetto come:

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

Ovviamente può fare come segue:

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

Tuttavia quando raggruppamento da oggetti con un sacco di proprietà, questo approccio è noioso e soggetto a errori.

Il codice sopra riportato genera NotSupportedException con il seguente messaggio: "Il tipo di selettore di chiavi per la chiamata al metodo" GroupBy "non è paragonabile nel provider di archivio sottostante". L'override di Equals e GetHashcode non ha alcun effetto. Immagino che il vero problema sia che il framework delle entità non sa come esprimere l'SQL ...?

+0

La parte 'MyObject' del modello EF? Posso fare una cosa simile con una proprietà di navigazione su 'MyObject' (dove MyObject: Item is n: 1). L'SQL generato è orribile, ma almeno funziona. –

+0

In realtà è un ComplexType. Quindi, in breve sì, EF è a conoscenza del tipo di oggetto. Poiché tutte le proprietà esistono effettivamente sulla stessa tabella, mi aspetto che l'SQL risultante sia molto più pulito di se stesse usando una proprietà di navigazione (se potessi farlo funzionare ovviamente!) – mindlessgoods

risposta

7

Pietosamente, no, non puoi farlo. ComplexType sembra promettente e sembra essere un bel "gruppo di colonne", ma il team EF lo ha semplicemente progettato per un uso completamente diverso e moltissime funzionalità legate al suo utilizzo come "gruppo di colonne" sono semplicemente mancanti.

Per quanto riguarda la mia conoscenza, "Ovviamente potrei fare quanto segue" è l'unico modo possibile. Devi solo definire il raggruppamento in base a una sottocolonna o una proiezione ad un tipo che il database saprà come confrontare.

Il problema principale è che la squadra EF vede il ComplexType non come un gruppo di colonne che consentono di riordinare il vostro entità con molti campi, ma, per loro, è un altro tipo di entità, tipo di uno transitorio. Il ComplexType è pensato per rappresentare oggetti che vengono restituiti per esempio da una stored procedure (o funzioni di ritorno della tabella) che restituisce un set di risultati di 5 colonne che non possono essere mappate su una tabella/classe esistente normale. Ad esempio, immagina di avere una tabella/classe Cliente che contiene 50 colonne/campi, la maggior parte di essi non null e che rappresentano alcuni dati sensibili. Si crea una stored procedure che prende un ID del cliente e restituisce il suo {nome, indirizzo, telefono di contatto, misura della scarpa}. Ovviamente, se si impostano tali risultati ritagliati, non è possibile decodificarli/mapparli alla classe Customer che ha tonnellate di campi che non possono essere nulli. Quindi, come rappresenti il ​​risultato in EF?

Prima opzione, piuttosto zoppa, è semplicemente ignorare EF e parlare direttamente al database e leggere/tradurre/decomprimere le righe/colonne a mano. Bene, conosciamo SqlClient, possiamo farlo.

La seconda opzione, brutta, consiste nel definire in EF una tabella/vista di stub, ad esempio CustomerView, che conterrà solo le colonne {nome, indirizzo, numero di telefono, numero di scarpe} e mappare il set di risultati della stored procedure ad esso. Funzionerebbe bene, ma se per caso 'generate database' formate il modello EF, otterrete anche quella tabella inutilizzata in più ..

Terza opzione per il salvataggio sono ComplexType. Invece di provare a creare una tabella o una vista shadow, devi solo dire all'EF che "c'è un tipo di dati extra" che non verrà mai mappato alla sua tabella e che non avrà mai definito un PrimaryKey e che l'EF per comodità, mappalo in una classe come qualsiasi altro oggetto dati. Ora, avendo questo tipo, è possibile restituirlo dalle procedure e recuperarlo come un oggetto ben digitato, è possibile passarlo come parametro della procedura, ecc. Ma: non è possibile mantenerlo da solo. Non ha una propria mappatura delle tabelle e nessuna propria chiave primaria.Ha nessuna identità.

È bello, ma bene, perché si desidera che tale tipo di dati venga restituito da una procedura? Probabilmente, la procedura ha elaborato o generato alcuni dati e si desidera che li memorizzi in alcune tabelle. Non hai definito un'entità intera normale, quindi quella cosa è solo un 'pacchetto dati', quindi probabilmente scriverai quel valore risultante su quella tabella insieme ad altri dati. Ed è per questo che l'EF ti permette di mappare ComplexType ad alcune colonne di una tabella: se hai ottenuto quell'oggetto risultato temporaneo da una procedura e ora vuoi scriverlo da qualche parte, non devi farlo manualmente colonna per colonna , basta mapparlo alle colonne "nome, indirizzo, telefono di contatto, dimensione della scarpa" del cliente.

Il punto più importante è che il server di database non conosce mai tale ComplexType esistente. Come ho detto, non aveva identità. Quando si tenta di fare una query LINQ come

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

allora, il complexValue è un oggetto CLR che non è mappata a qualsiasi tavolo, non ha identità, quindi NO PK, quindi EF ha completamente idea di come controllare se l '"oggetto della mano sinistra" è lo stesso di "oggetto della mano destra". Se gli oggetti avessero definito un'identità, potrebbe controllare PK sul lato server, o fare un confronto di oggetti di riferimento sul lato client, ma qui - semplicemente fallisce.

Sono assolutamente d'accordo sul fatto che questa è un'altra caratteristica dannatamente utile nell'EF che manca. Credo che sarebbe estremamente facile per il team EF confrontare i ComplexTypes su base colonna per colonna, ma non è così. Semplicemente non pensavano che ComplexTypes potesse essere usato per mettere in ordine i tavoli. Hanno significato che fosse un pacchetto di dati temporaneo da passare a e da stored procedure e funzioni .. Peccato.

+0

una nota a margine: dico riguardo al confronto , perché recentemente mi sono imbattuto in questo quando ho provato a filtrare/dove su un tipo complesso. Il raggruppamento è molto simile, tutto si riduce a un problema "non so come confrontare". Se qualcuno conosce soluzioni alternative o estensioni EF, sarei più che felice di saperlo. Per ora, ho trovato solo articoli che dicevano "scusa, questo non è ancora disponibile, forse in EF5" – quetzalcoatl

+0

Grazie per la risposta approfondita - è un'altra caratteristica che sarebbe bello vedere nelle versioni future. – mindlessgoods

Problemi correlati