Ho scritto un metodo che estende la classe System.Linq
. Il metodo My ContainsAnyWord
mi consente di cercare tutte le parole di una stringa su una stringa anziché confrontare una stringa con un'altra.
Ecco il metodo che ho scritto per estendere Linq
public static class LinqExtension
{
public static bool ContainsAnyWord(this string value, string searchFor)
{
if (!string.IsNullOrWhiteSpace(value) && !string.IsNullOrWhiteSpace(searchFor))
{
value = value.ToLower();
IEnumerable<string> tags = StopwordTool.GetPossibleTags(searchFor);
return tags.Contains(value);
}
return false;
}
}
Questo metodo di estensione funziona alla grande su un oggetto IEnumerable
. Ma quando voglio usarlo su oggetti IQueryable
ottengo il seguente errore
LINQ to Entities non riconosce il metodo 'Boolean ContainsAnyWord (System.String, System.String)' e questo metodo non può essere tradotto in un'espressione di archivio.
Il seguente esempio funziona alla grande perché sto lavorando con una lista.
using(var conn = new AppContext())
{
var allUsers = conn.Users.GetAll().ToList();
var foundUsers = allUsers.Where
(
user =>
user.FirstName.ContainsAnyWord("Some Full Name Goes Here")
|| user.LastName.ContainsAnyWord("Some Full Name Goes Here")
).ToList();
}
Ma il seguente esempio non funziona perché sto lavorando con IQueryable
che mi dà l'errore sopra elencato.
using(var conn = new AppContext())
{
var allUsers = conn.Users.Where
(
user =>
user.FirstName.ContainsAnyWord("Some Full Name Goes Here")
|| user.LastName.ContainsAnyWord("Some Full Name Goes Here")
).ToList();
}
Come posso risolvere questo problema e rendere questo metodo disponibile per IQueryable
oggetto IQueryable
?
AGGIORNATO
Sulla base del feedback che ho ottenuto di seguito, ho cercato di implementarlo utilizzando IQueryable
questo modo
public static IQueryable<T> ContainsAnyWord<T>(this IQueryable<T> query, string searchFor)
{
if (!string.IsNullOrWhiteSpace(searchFor))
{
IEnumerable<string> tags = StopwordTool.GetPossibleTags(searchFor);
return query.Where(item => tags.Contains(item));
}
return query;
}
Ma anche questo mi sta dando un errore
Devi tenere a mente che questo deve essere tradotto in SQL alla fine. Ovviamente ContainsAnyWord non può essere tradotto in SQL ...
Quindi, salva i tuoi nomi in una Lista / Matrice e prova
user => yourlist.Contains( user.FirstName)
EF tradurrà questo in un WHERE ..IN
Non c'è bisogno di un metodo, ma se per qualche ragione lo vuoi
internal static class MyClass2
{
public static IQueryable<T> ContainsAnyWord<T>(this IQueryable<T> value, string searchFor) where T: User
{
var names = searchFor.Split(' ').ToList();
return value.Where(u => names.Contains(u.DisplayName));
}
}
Puoi usarlo come
var result=conn.Users.ContainsAnyWord("abc def ght");
Non è possibile chiamare metodi all'interno dell'espressione query il parser di espressioni fornito da Linq alle Entità non sa come gestire. L'unica cosa che puoi veramente fare è scrivere un metodo di estensione che IQueryable<T>
un IQueryable<T>
e costruisca l'espressione internamente all'interno della funzione invece di chiamare Where(
Questo codice qui sotto è assolutamente non testato ed è qualcosa che mi è venuto fuori in cima alla mia testa. Ma avresti bisogno di fare qualcosa del genere
public static IQueryable<T> WhereContainsAnyWord<T>(this IQueryable<T> @this, Expression<Func<T, string>> selector, string searchFor)
{
var tags = StopwordTool.GetPossibleTags(searchFor); //You might need a .ToArray() here.
var selectedItems = @this.GroupBy(selector);
var filteredItems = selectedItems.Where(selectedItem => tags.Contains(selectedItem.Key.ToLower()));
var result = filteredItems.SelectMany(x => x);
return result;
}
usato come
using(var conn = new AppContext())
{
var allUsers = conn.Users.WhereContainsAnyWord(user => user.FirstName, "Some Full Name Goes Here").ToList();
}
Ecco una versione non generica di un simplificatore
public static IQueryable<User> WhereContainsAnyWord(this IQueryable<User> @this, string searchFor)
{
var tags = StopwordTool.GetPossibleTags(searchFor); //You might need a .ToArray() here.
return @this.Where(user => tags.Contains(user.DisplayName.ToLower()));
}
usato come
using(var conn = new AppContext())
{
var allUsers = conn.Users.WhereContainsAnyWord("Some Full Name Goes Here").ToList();
}