2012-08-27 13 views
7

Prevedo questo con sto cercando attivamente la soluzione a questo problema ma ho pensato che potrei abbreviare alcuni tempi di ricerca e sviluppo se qualcuno qui in pila lo ha già capito. (Non ho trovato nulla online, ecco qui)Concatenamento O condizioni in EF 5.0

Abbiamo un caso in un framework di applicazione che stiamo costruendo dove abbiamo bisogno della capacità di prendere in una serie di Predicati (List<Expression<Func<T,bool>>>) e analizzarlo in un framework di ricerca.

In questo momento abbiamo la capacità di filtrare in questo modo è che:

//Assume predicates is passed as a method argument. 
//  of List<Expression<Func<T,bool>>> 
//Assume user is passed in as a method argument. 
//Assume FilterToUserAccess is a custom extension method that restricts the dataset 
// to access restrictions. 
var query = _dbContext.Set<EntityType>() 
    .FilterToUserAccess(user); 
foreach(var p in predicates){ 
    query = query.Where(p); 
} 

return p.ToList(); 

Il motivo abbiamo bisogno di fare questo è per scale-capacità di oggetti filtrabili. Tuttavia per una ricerca rapida questo non è possibile date le funzionalità integrate di EF. Quello che devo essere in grado di fare è:

Oggetto A (facciamo finta che sia un'auto da corsa) e vogliamo cercare marca, modello, squadra e autista in una casella di ricerca rapida. Quindi, se inserisco "Earnhardt", cercherebbe tutte le proprietà delle entità delle auto da corsa come marca, modello, squadra e conducente. Vorrei finire con tutte le vetture dei DEI così come con Dale Jr. Vorrei utilizzare lo stesso approccio in modo da poter configurare un'entità ricercabile e riflettere la configurazione di ricerca all'avvio dell'applicazione. Vorrei ideale per fare qualche modo di avere la query simile a questo:

//Assume predicates is passed as a method argument. 
//  of List<Expression<Func<T,bool>>> 
//Assume user is passed in as a method argument. 
//Assume FilterToUserAccess is a custom extension method that restricts the dataset 
// to access restrictions. 
var query = _dbContext.Set<EntityType>() 
    .FilterToUserAccess(user); 
foreach(var p in predicates){ 
    query = query.Or(p); 
} 

return p.ToList(); 

mi rendo conto che posso fare:

_dbContext.Set<EntityType>().Where(predicate1 || predicate2 || predicate3) 

Tuttavia questo non funzionerà per l'approccio che vogliamo prendere per risolvi questo problema. Idealmente un amministratore per uno dei nostri siti clienti sarebbe in grado di entrare e configurare un termine di ricerca aggiuntivo con un solo clic per essere incluso in tutte le ricerche rapide per quel tipo di entità come possiamo attualmente utilizzare Filtri che utilizzano lo standard .Where(...) "e" logica di concatenamento.

+0

Sarà necessario creare un albero di espressioni in modo dinamico. –

+0

Qualche possibile punto di partenza per questo? Conosco le basi della mano che generano un albero delle espressioni ma temo che questa tana del coniglio vada piuttosto in profondità molto velocemente. Qualsiasi riferimento sarebbe molto apprezzato. – VulgarBinary

+2

È possibile verificare LinqKit/Expression Builder. EB funziona per EF e rende piuttosto facile concatenare le condizioni di 'OR'. –

risposta

6

La prima soluzione era un busto, tuttavia con alcuni più scavi c'è una soluzione incredibilmente semplice, verificata e funzionante.

Passaggio 1: installare il pacchetto NuGet per LinqKit.

Fase 2: Godetevi il codice qui sotto

using (ISampleRepository repo = new SampleRepository()) 
{ 
    var predicates = new List<Expression<Func<Customer,bool>>>(){ 
     (x => x.FirstName.Contains(searchValue)), 
     (x => x.LastName.Contains(searchValue)) 
    }; 

    var lambda = PredicateBuilder.False<Customer>(); 
    lambda = predicates.Aggregate(lambda, (current, p) => current.Or(p).Expand()); 

    var query = repo.QueryCustomers().AsExpandable().Include(x => x.Phones).Where(lambda); 
    return query.Take(500) 
     .ToList() 
     .Select(x => x.ToDTO()) 
     .ToList(); 
} 

Questo è solo il campione di picco, ma facendo la stessa cosa con un metodo di prendere in ->

List<T> QuickSearch<T>(string input) ... 

sarà in grado di utilizzare lo stesso approccio. Si dispone ancora di una serie di predicati nel modulo Espressione passato, quindi si utilizzano i suggerimenti del builder del predicato per disattivare la query. Quindi utilizzando AsExpandable() consente di eseguire il predicato combinato creato utilizzando il generatore di predicati.

Speriamo che questo sia utile a più di me, ma questa è la soluzione con cui sto andando in quanto è un po 'meno codice. Ti consente di costruire i tuoi predicati altrove ... e di combinarli in una "OR" dopo il fatto.

+0

Hai provato questo contro una memoria ancora interrogabile? Sarei preoccupato che l'espressione del parametro non rimanga nell'ambito di applicazione tra ciascuna espressione. – mclark1129

+0

Nel processo di test mentre parliamo. Le espressioni parametriche usano le costanti per formare l'elenco dei predicati. L'altro è purché il param sia definito come la stessa variabile che rimane nell'ambito. Questo lo so dal voodoo che abbiamo ottenuto con l'approccio Filter. La mia unica preoccupazione qui è l'OR incatenato con un problema e non la compilazione in SQL nativo. Aggiornerò appena verificherò se funziona o meno. – VulgarBinary

+0

Questo è probabilmente solo un refuso, ma la tua espressione eseguibile contiene 'Func ' mentre il resto dell'esempio è generico 'T'. – mclark1129

2

Come dice Ladislav, è necessario generare dinamicamente le espressioni LINQ.Ecco un semplice esempio di un programma che costruisce dinamicamente un predicato per una collezione di numeri interi:

class Program { 
    static void Main(string[] args) { 

     // Retreive your data source 
     List<int> numbers = new List<int>() { 0, 10, 20, 30, 40, 50, 60 }; 

     // Create a collection of predicates that you would like to chain together. 
     ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "x"); 
     List<Expression> predicates = new List<Expression>(); 

     // x >= 50 
     predicates.Add(Expression.GreaterThanOrEqual(parameterExpression, Expression.Constant(50))); 

     // x <= 20 
     predicates.Add(Expression.LessThanOrEqual(parameterExpression, Expression.Constant(20))); 

     // Build a single predicate by chaining individual predicates together in an OR fashion 
     Expression whereFilter = Expression.Constant(false); // Use false a base expression in OR statements 

     foreach (var predicate in predicates) { 
      whereFilter = Expression.OrElse(whereFilter, predicate); 
     } 

     // Once the expressions have been chained, create a lambda to represent the whole predicate 
     // x => (x >= 50) || (x <= 20) 
     Expression<Func<int, bool>> whereLambda = 
      (Expression<Func<int, bool>>)Expression.Lambda(whereFilter, 
              new List<ParameterExpression>() { parameterExpression }); 

     // To use an expression directly, the datasource must be an IQueryable 
     // Since I am using List<T> I must call AsQueryable. This is not necessary 
     // if your collection is already IQueryable, like in Entity Framework. 
     var results = numbers.AsQueryable().Where(whereLambda); 

    } 
} 

In sostanza tutto quello che faccio qui è creare diversi statments booleani (x> = 50) e (x < = 20) e metterli in una collezione. Quindi, collegando quella raccolta, prendo ogni affermazione e O l'ultima. Il risultato è una serie di istruzioni booleane collegate tra loro da OR. Quindi avvolgo quella frase in un'espressione Lambda in modo che possa essere consumata da IQueryable.Where e passarla alla mia collezione interrogabile. I risultati sono un insieme filtrato di numeri interi dal mio set completo.

LINQ Le espressioni possono essere poco chiare a dir poco, ma sono incredibilmente potenti e utili da conoscere. Per favore fatemi sapere se c'è qualcosa che posso fare per aiutare a dare più senso a questo esempio.

+0

"esempio semplice" .. –

+0

bump per una risposta molto ben elaborata e solo pochi istanti dopo il mio arrivo. Ancora molto bene messo insieme e molto dettagliato. Grazie! – VulgarBinary

+0

Sono d'accordo, le espressioni LINQ hanno una sintassi abbastanza dettagliata e confusa. Direi che è piuttosto semplice per quanto riguarda le espressioni LINQ, concatenando solo alcune semplici espressioni booleane. – mclark1129