C’è un modo semplice per scrivere una funzione personalizzata in LINQ alle entity framework?

Sto scrivendo una semplice query di ricerca per la mia applicazione Entity Framework. Devo controllare se un gruppo di campi sono nulli e, in caso contrario, chiamare ToLower () su di essi e confrontarli con la query di ricerca. La query LINQ è simile a questa:

public IQueryable SearchStores(string q, IQueryable source) { q = q.ToLower(); return ( from s in source where ( (s.Name != null && s.Name.ToLower().Contains(q)) || (s.Description != null && s.Description.ToLower().Contains(q)) || ... } 

Ci sono molte righe come questa, quindi sono stato tentato di scrivere un metodo di supporto per ripulirlo un po ‘:

 public static bool SafeSearch(this string s, string q) { return s == null ? false : s.ToLower().Contains(q); } 

Questo, naturalmente, non funziona, poiché LINQ per le quadro non capisce cosa sia la funzione SafeSearch:

LINQ to Entities non riconosce il metodo metodo “Boolean SafeSearch (System.String, System.String)” e questo metodo non può essere tradotto in un’espressione di archivio.

C’è un modo semplice per scrivere una semplice funzione personalizzata come questa?

Grazie!

Poiché linq utilizza espressioni che non vengono eseguite fino a quando non si chiama effettivamente il database, è necessario avvolgere la funzione all’interno di un predicato.

 private static Func Predicate(string q) { return x => ( q.SafeSearch(x.Name) || q.SafeSearch(x.Description) ); } 

Anche invertendo il metodo di estensione SafeSearch chiamandolo su query, si prenderà cura dei casi in cui x.Name è nullo.

 public static class SearchExt { public static bool SafeSearch(this string q, string param) { return param == null ? false : param.ToLower().Contains(q); } } 

e quindi potresti usarlo con i metodi di estensione

 return source.Where(Predicate(q)); 

o usando l’espressione linq

 return from p in source where Predicate(q).Invoke(p) select p; 

C’è un modo per preparare query e condizioni dinamiche e anche usare le funzioni per costruirne parti. La syntax è anche leggibile, cosa che farebbe per la parte “semplice” della domanda. È ansible combinando le espressioni di Linq . Ci sono diversi articoli su come questo può essere fatto, ma penso che sia venuto fuori con un nuovo approccio. Almeno non l’ho trovato sul web.

Per procedere è necessaria una libreria di 3 semplici funzioni. Usano System.Linq.Expressions.ExpressionVisitor per modificare dynamicmente le espressioni. La caratteristica fondamentale è unificare i parametri all’interno dell’espressione, in modo tale che 2 parametri con lo stesso nome siano stati resi identici ( UnifyParametersByName ). La parte rimanente sta sostituendo un parametro denominato con un’espressione data ( ReplacePar ) e un metodo helper ( NewExpr ). La libreria è disponibile con licenza MIT su github: LinqExprHelper , ma è ansible scrivere velocemente qualcosa da soli.

Prima si definiscono alcuni metodi, che possono essere successivamente utilizzati nella creazione di query dinamiche.

 public class Store { ... public static Expression> SafeSearchName(string sWhat) { return LinqExprHelper.NewExpr( (Store s) => s.Name != null && s.Name.ToLower().Contains(sWhat) ); } public static Expression> SafeSearchDesc(string sWhat) { return LinqExprHelper.NewExpr( (Store s) => s.Description != null && s.Description.ToLower().Contains(sWhat) ); } } 

Quindi esegui una query in questo modo:

  // Define a master condition, using named parameters. var masterExpr = LinqExprHelper.NewExpr( (Store s, bool bSearchName, bool bSearchDesc) => (bSearchName && bSearchDesc)); // Replace stub parameters with some real conditions. var combExpr = masterExpr .ReplacePar("bSearchName", Store.SafeSearchName("b").Body) .ReplacePar("bSearchDesc", Store.SafeSearchDesc("p").Body); // Sometimes you may skip a condition using this syntax: //.ReplacePar("bSearchDesc", Expression.Constant(true)); // It's interesting to see how the final expression looks like. Console.WriteLine("expr: " + combExpr); // Execute the query using combined expression. db.Stores .Where((Expression>)combExpr) .ToList().ForEach(i => { Console.WriteLine(i.Name + ", " + i.Description); }); 

Non l’ho ancora usato in produzione, ma sono passati alcuni test semplici. Non vedo alcun limite nel combinare le query in questo modo. Se abbiamo bisogno di più parametri, possiamo aggiungere un ulteriore livello di combinazione. Il vantaggio di questo metodo è che puoi usare espressioni lambda inline, che sono piacevoli da leggere, insieme alla creazione di espressioni dinamiche e alla composizione, che è molto capace.

È “semplice” dopotutto? Se si considera la syntax dei metodi di Linq come semplice, allora è quasi così semplice. Non consente di creare funzioni personalizzate di Linq, ma offre funzionalità comparabili.