2015-06-01 14 views
6

Sembra che Linq to Entities non supporti il ​​metodo Aggregate. Devo riscrivere questa parte di un'espressione più grande a .Select(a => a.Permissions).Aggregate((a, b) => a | b) a qualcosa che Linq alle Entità comprende. È possibile?Collegamento a entità Sostituzione del metodo aggregato

Forse dovrei darti una spiegazione migliore di quello che sto facendo. Devo ottenere una raccolta di Organizzatori dal database in base alle autorizzazioni dell'utente per questi Organizzatori. Le autorizzazioni vengono memorizzate come flag di bit e sono una combinazione dei seguenti elementi:

  1. DefaultOrganizerPermissions - Ogni organizzatore ha alcune autorizzazioni predefinite
  2. OwnerPermissions - Se l'utente è il proprietario del organizzatore, ha qualche ulteriore permesso
  3. UserPermisions - Gli utenti possono essere assegnati manualmente per avere alcune autorizzazioni per Organizer
  4. RolePermissions - Gli utenti possono essere membri di ruoli e questi ruoli possono essere assegnati per avere autorizzazioni aggiuntive per gli Organizzatori.

Così ho un metodo con la seguente firma

public IQueryable<Organizer> GetOrganizers(Person person, OrganizerPermissions requiredPermissions)

e al suo interno v'è un codice come questo

organizers = organizers.Where(o => ((
     // Default permissions 
     DefaultOrganizerPermissions | 
     // Owner permissions 
     (o.OwnerId == person.Id ? OwnerOrganizerPermissions : 0) | 
     // Personal permissions 
     (o.AccessIdentifier.Accesses.FirstOrDefault(a => a.Accessor is PersonalAccessor && (a.Accessor as PersonalAccessor).PersonId == person.Id) != null ? 
     (OrganizerPermissions)o.AccessIdentifier.Accesses.FirstOrDefault(a => a.Accessor is PersonalAccessor && (a.Accessor as PersonalAccessor).PersonId == person.Id).Permissions : 0) | 
     // Role permissions 
     (o.AccessIdentifier.Accesses.Any(a => a.Accessor is RoleAccessor && (a.Accessor as RoleAccessor).Role.RoleMembers.Any(rm=>rm.PersonId == person.Id)) ? 
     (OrganizerPermissions)o.AccessIdentifier.Accesses.Where(a=> a.Accessor is RoleAccessor && (a.Accessor as RoleAccessor).Role.RoleMembers.Any(rm => rm.PersonId == person.Id)).Select(a=>a.Permissions).Aggregate((a, b) => a | b) : 0) 
     ) & requiredPermissions) == requiredPermissions); 

semplificata è qualcosa di simile:

organizers = organizers.Where(o => ((
     DefaultOrganizerPermissions | 
     OwnerOrganizerPermissions | 
     UserPermisions | 
     RolePermissions 
     ) & requiredPermissions) == requiredPermissions); 

Il problema è che l'utente può essere membro di più ruoli, quindi RolePermissions è in realtà più permessi e ho bisogno di appiattirlo con OR bit a bit. Ma come, se Aggregate non è supportato?

risposta

1

Se la quantità di dati non è proibitivo grande, si potrebbe considerare chiamando .ToList() prima dell'istruzione Select(...) per leggere i dati in memoria e quindi eseguire l'aggregato LINQ to Objects

+0

Ho provato prima, ma non ha alcun effetto. –

+0

Hai provato questo? .... ToList(). Select (a => a.Permissions) .Aggregate ((a, b) => a | b) ToList() materializza la query (quindi è cattiva) ma dovrebbe funzionare ... avere anche altri modi, come Sum o selezionarne solo uno vero (FirstOrDefault) ma dovresti essere più esplicito. – bubi

+0

@bubi Sì, l'ho fatto. Sto usando acutamente ToList() in un altro metodo della stessa classe e funziona. Ma non funziona in questo. Penso che sia strano, mi aspetterei che funzionasse, o di sollevare qualche (diverso) errore, ma non ci sono cambiamenti. Ho solo bisogno in qualche modo di unire più flag di bit (sono rappresentati come numeri interi), come ad esempio 1010 | 0011 = 1011. Non so se è possibile con Sum. –

0

se le autorizzazioni è annullabile probabilmente avete bisogno inserire
a.Permissions.GetValueOrDefault()
modo che la query dovrebbe essere
.Selezionare (a => a.Permissions.GetValueOrDefault()) Aggregate ((a, b) => a | B).

Inoltre, se il provider EF non supporta Traduzione aggregata in SQL (questa parte è specifica del provider in modo che possa funzionare con un DBMS ma non con un DBMS diverso) è necessario materializzare la query con ToList() (come suggerito da @sjager).

EDIT
Il problema è che fornitore di EF non supporta traduzioni aggregata a SQL.

Io non sono un esperto di SQL Server in ogni caso si dovrebbe trovare un aggregata funzione (come SUM o AVG) che ti fa ciò che è necessario (OR bit a bit). In MySQL è BIT_OR().

Se esso non esiste, allora GAME OVER

Se esiste è possibile visualizzare il codice sorgente di fornitore di EF per SQL Server e funzione di visualizzazione wich LINQ è tradotto in quella o chiedere un'altra Question-

+0

Autorizzazioni non annullabili, ha sempre un valore. Posso eseguire correttamente la parte aggregata dopo ToList(), se è l'ultima istruzione dell'albero delle espressioni. Ma ho bisogno di eseguirlo come parte di una query più ampia, cioè nel mezzo dell'albero delle espressioni, questo è il problema. –

+0

Qual è l'eccezione se non si inserisce ToList? O il problema è SQL Server? In questo caso è possibile tracciare la query e provare a eseguirla su SQL Server e vedere cosa succede. – bubi

+0

Si tratta di un'eccezione di tipo System.NotSupportedException verificatasi in EntityFramework.SqlServer.dll ma non è stata gestita nel codice utente Ulteriori informazioni: LINQ to Entities non riconosce il metodo 'Int32 Aggregate [Int32] (System.Collections .Generic.IEnumerable''1 [System.Int32], System.Func''3 [System.Int32, System.Int32, System.Int32]) 'metodo, e questo metodo non può essere tradotto in un'espressione di archivio. Collegamento a Le entità semplicemente non sanno come tradurre il metodo Aggregate in SQL. –

0

Sorprendentemente , Mi sono imbattuto nella stessa identica situazione di ieri. Quindi, come dichiarato da @bubi, senza una funzione di aggregazione corretta del server SQL è possibile non possibile.

Il modo in cui ho lavorato è creare un gruppo di gruppi di espressioni OR per ogni ruolo, ciascuno con un confronto con un ruolo dell'utente. Quindi, connettere tutti i "gruppi di espressioni OR" con il predicato AND.

E.g. se l'utente ha il valore enum di 0011, allora sarà necessario creare predicati OR per entrambi i flag 0001 e 0010. 0001 flag "OR group" assomiglia a

x=> x.Equals(0001) || x.Equals(0011) || x.Equals(0101)| || ..) 

Ma non è assolutamente consigliato.

Problemi correlati