Voglio applicare una serie di filtri compositi a un gruppo di dati come questo:
Filter[] filters = new[] { new Filter { Name = "Bob", Gender = "Male" },
new Filter { Name = "Alice", Height = "Female" } };
_dbContext.People.Where(p => filter.Any(f => f.Name == p.Name && f.Gender == p.Gender)).Select(p => p.Id);
Sono interessato agli Ids
di Male Bobs e Female Alices . Mi dispiace Bobs femminile . Non ti voglio
Questo è il modo corretto di risolvere quel problema nella memoria Linq, ma c'è un problema. Ecco come si presenta SQL EF (sto verificando questo nel mio profiler del server SQL)
SELECT [p].[Name], [p].[Gender], [p].[Id] FROM [People] AS [p]
È terribile. Scava tutto e poi fa il vero lavoro in memoria. Non c'è modo che questo funzioni con molte persone, si fermerà.
C'è un modo per rendere l'aspetto di SQL generato più simile a questo?
SELECT
[Person].[Id]
FROM [Person]
WHERE
((([Person].[Name] = "Bob") AND ([Person].[Gender] = "Male"))
OR (([Person].[Name] = "Alice") AND ([Person].[Gender] = "Female")))
(come è possibile in Dapper )
Ecco cosa ho fatto alla fine, come suggerito da @stuartd:
var predicate = PredicateBuilder.New<Person>();
foreach (var filter in filters)
{
predicate = predicate.Or(p =>
p.Gender == filter.Gender && filter.Name == p.Name));
}
people = _dbContext.People.Where(predicate).Select(r => r.Id).Distinct().ToArrayAsync();
Funziona come un fascino. Grazie.
La struttura della query suggerisce che il numero di combinazioni nome / sesso potrebbe essere superiore a due. In tal caso, potrebbe essere più sensato scrivere la propria procedura memorizzata anziché consentire a EF di creare la query.
In questo caso, utilizzerei un parametro con valori di tabella per passare un set di righe come parametri alla procedura memorizzata. Ogni riga contiene sia un nome che un genere. La query unisce quel parametro della tabella alla tabella Person, restituendo le righe in cui entrambi i nomi e i sessi corrispondono.