2011-08-16 3 views
19

Ecco il problema: Devo restituire una raccolta di oggetti con raccolte nidificate filtrate. E.g: c'è un negozio con ordini e ho bisogno di restituire una collezione con negozi che include collezioni annidate con ordini ma senza ordini da parte di clienti che sono contrassegnati come cancellati.Come filtrare gli oggetti Entity Framework di raccolta nidificata?

Ecco quello che cerco di fare. Ma ancora senza fortuna. Qualsiasi suggerimento è appreso :)

public List<StoreEntity> GetStores(Func<Store, bool> storeFilter, Predicate<OrderEntity> orderFileter) 
{ 
    IQueryable<StoreEntity> storeEntities = Context.Stores 
     .Include(o => o.Order) 
     .Include(cu => cu.Orders.Select(c => c.Customer)) 
     .Where(storeFilter) 
     //.Where(rcu=>rcu.Orders.Select(cu=>cu.Customer.Deleted==false)) //just test this doesn't work 
     .AsQueryable(); 

    List<StoreEntity> storeEntities = storeEntities.ToList(); 

    //storeEntities.ForEach(s => s.Orders.ToList().RemoveAll(c=>c.Customer.Deleted==true)); // doesn't work 

    foreach (StoreEntity storeEntity in storeEntities) 
    { 
     storeEntity.Orders.ToList().RemoveAll(r=>r.Customer.Deleted==true); 
    } 

    return storeEntities; 
} 

Il problema è che il filtro non viene applicato. I clienti che hanno il flag eliminato impostato su true rimangono nella raccolta.

+0

E qual è il problema? Non si compila? Genera un'eccezione di runtime?Esegue ma restituisce i dati errati? –

+0

ha spiegato un po 'di più. grazie. –

+1

Ho finito per utilizzare questo pacchetto nuget: 'Z.EntityFramework.Plus.QueryIncludeFilter.EF6' Documentazione qui: https://github.com/zzzprojects/EntityFramework-Plus/wiki/EF-Query-IncludeFilter-%7C-Entity- Framework-Include-Related-Entities-using-Where-Filter –

risposta

26

Non puoi farlo direttamente in un modo "pulito", ma hai alcune opzioni.
Prima di tutto, è possibile esplicitamente caricare la raccolta secondaria dopo aver recuperato i negozi. Vedi la sezione Applying filters when explicitly loading related entities.

Se non si desidera eseguire ulteriori viaggi nel database, sarà necessario creare la propria query e proiettare manualmente la raccolta principale e le raccolte secondarie filtrate su un altro oggetto. Vedere le seguenti questioni esempi:
Linq To Entities - how to filter on child entities
LINQ Query - how sort and filter on eager fetch

Modifica

Tra l'altro, il primo .Where(rcu=>rcu.Orders.Select(cu=>cu.Customer.Deleted==false)) tentativo non funziona in quanto in questo modo si sta applicando un filtro per la vostra collezione di genitore (negozi) piuttosto che la raccolta nidificata (ad esempio tutti i negozi che non hanno clienti eliminati).
Logicamente, il codice che filtra la raccolta nidificata deve essere inserito nel metodo Include. Attualmente, Include supporta solo una dichiarazione Select, ma personalmente penso che sia arrivato il momento per il team EF di implementare qualcosa di simile:

.Include(cu => cu.Orders.Select(c => c.Customers.Where(cust => !cust.IsDeleted))); 
+0

Sì, sono d'accordo. (So ​​che quella cosa non funziona, ma volevo ancora provarla, chissà che quella caratteristica fosse nascosta e sarei rimasta piacevolmente sorpresa), e sì, questo tipo di Includi sarebbe bello avere. Ho deciso di andare con la proiezione personalizzata ... non mi piace molto questo metodo, perché ho dovuto assegnare molti valori stupidi come (ID = r.ID, Name = r.Name ... e così via). Ma almeno funziona :) Thx –

+3

Sono d'accordo che il team EF dovrebbe implementare l'inclusione filtrata. Vota per questo [qui] (https://entityframework.codeplex.com/workitem/47)! – Chris

+0

Che cosa succede se ho un metodo generico chiamato 'GetWithInclude()' ' pubblico IQueryable GetWithInclude (params Expression > [] includeProperties) { ritorno includeProperties.Aggregate >, IQueryable > (DbSet, (current, includeProperty) => current.Include (includeProperty)); } ' Eppure, non ho una collezione e ho bisogno di un filtro da una proiezione. In altre parole, ho una proprietà che deve eguagliare qualcosa. – Vyache

2

Il problema con il codice attualmente si dispone è questa linea:

storeEntity.Orders.ToList().RemoveAll(r=>r.Customer.Deleted==true); 

storeEntity.Orders.ToList() restituisce un nuovoList<OrderEntity> con il contenuto di storeEntity.Orders. Da questo nuovo elenco, rimuovi tutti i clienti eliminati. Tuttavia, questo elenco non viene utilizzato da nessuna parte dopo.

Tuttavia, anche se farebbe ciò che si desidera, rimuoverà anche tali clienti dal database, poiché gli oggetti StoreEntity sono ancora connessi al contesto dati!

Si desidera veramente utilizzare un filtro come si è provato nel commento Where. Per favore, guarda la risposta di Yakimych su questo.

-5

Vecchio argomento, ma mi sono imbattuto in un problema piuttosto simile. Ho cercato molto, e il collegamento MSDN fornito da Yakimych mi ha finalmente suggerito una soluzione: disabilitare esplicitamente il caricamento lento e quindi eseguire query per filtrare le proprietà di navigazione. Il risultato sarà quindi "allegato" alla query principale, che darebbe qualcosa del genere:

Context.Configuration.LazyLoadingEnabled = false; 

var filteredOrders = Context.Orders.Where(x => x.Customer.Delete == false); 

IQueryable<StoreEntity> storeEntities = Context.Stores 
.Include(o => o.Order) 
.Include(cu => cu.Orders.Select(c => c.Customer)) 
.Where(storeFilter) 
.AsQueryable(); 
+0

Che cos'è storeFilter? –