Ricerca jolly per LINQ
Domanda
Vorrei sapere se è possibile effettuare una ricerca con caratteri jolly usando LINQ.
Vedo che LINQ contiene Conts, StartsWith, EndsWith, ecc.
Cosa succede se voglio qualcosa come% Test se% funziona%, come posso farlo?
Saluti
Soluzione
Vorrei usare le espressioni regolari, poiché potresti non utilizzare sempre Linq to SQL.
Come questo esempio di Linq to Objects
List<string> list = new List<string>();
list.Add("This is a sentence.");
list.Add("This is another one.");
list.Add("C# is fun.");
list.Add("Linq is also fun.");
System.Text.RegularExpressions.Regex regEx = new System.Text.RegularExpressions.Regex("This");
var qry = list
.Where<string>(item => regEx.IsMatch(item))
.ToList<string>();
// Print results
foreach (var item in qry)
{
Console.WriteLine(item);
}
Altri suggerimenti
Puoi utilizzare SqlMethods.Like () .
Un esempio dell'uso:
var results =
from u in users
where SqlMethods.Like(u.FirstName, "%John%")
select u;
aggiungi System.Data.Linq.SqlClient all'elenco di utilizzo o importazione, quindi prova:
var results= from x in data
where SqlMethods.Like(x.SearchField, “%something%like%this%”)
select x;
Per Entity Framework Core 2.0
è presente l'operatore LIKE
( annunciato nell'agosto 2017 ):
var query = from e in _context.Employees
where EF.Functions.Like(e.Title, "%developer%")
select e;
Guardando la domanda
Cosa succede se voglio qualcosa come% Test se% funziona%, come posso farlo?
allora mi aspetto qualcosa di
LIKE '%Test if%it work%'
significa che la stringa deve contenere "Prova se" e "funziona", in questo ordine .
Questo non funzionerà:
context.SomeTable.Where(s => s.Name.Contains("Test if%it work")).ToList();
E se uso:
context.SomeTable.Where(s => s.Name.Contains("Test if") && s.Name.Contains("it work")).ToList();
quindi troverò tutti i record che contengono sia " Test if " e " funziona " ;, ma non specificamente in quell'ordine .
Quindi con Contiene questo non è possibile. Ma con IndexOf lo è.
IndexOf individuerà la stringa di ricerca e ne restituirà la posizione nella stringa. Rendere possibile trovare le parole nell'ordine corretto.
- Aggiorna -
Con la mia risposta originale non era il mio obiettivo fornire una soluzione generica, ma piuttosto un esempio di un altro approccio che non dipende da sql. Quindi è corretto che l'esempio originale risponda solo alla domanda letterale. Ma poiché la risposta può essere più utile se è generica, ho scritto un'estensione IQuerable che consente di aggiungere un'istruzione simile alla query semplice come un'istruzione where. L'estensione funziona sia per Linq che per Linq-Sql.
Questo troverà tutti i record con entrambi " Test if " e " funziona " ;, in questo ordine.
context.SomeTable.Like("test if%it work", "Name").ToList();
listOfString.Like("test if%it work").ToList();
Estensione, consente qualsiasi numero di caratteri jolly:
/// <summary>
/// Allow to search the string with wildcards.
/// </summary>
/// <typeparam name="T">String or an object with a string member.</typeparam>
/// <param name="q">Original query</param>
/// <param name="searchstring">The searchstring</param>
/// <param name="memberName">The name of the field or null if not a field.</param>
/// <returns>Query filtered by 'LIKE'.</returns>
public static IQueryable<T> Like<T>(this IQueryable<T> q, string searchstring, string memberName = null)
{
// %a%b%c% --> IndexOf(a) > -1 && IndexOf(b) > IndexOf(a) && IndexOf(c) > IndexOf(b)
var eParam = Expression.Parameter(typeof(T), "e");
MethodInfo methodInfo;
// Linq (C#) is case sensitive, but sql isn't. Use StringComparison ignorecase for Linq.
// Sql however doesn't know StringComparison, so try to determine the provider.
var isLinq = (q.Provider.GetType().IsGenericType && q.Provider.GetType().GetGenericTypeDefinition() == typeof(EnumerableQuery<>));
if (isLinq)
methodInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string), typeof(StringComparison) });
else
methodInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) });
Expression expr;
if (string.IsNullOrEmpty(memberName))
expr = eParam;
else
expr = Expression.Property(eParam, memberName);
// Split the searchstring by the wildcard symbol:
var likeParts = searchstring.Split(new char[] { '%' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < likeParts.Length; i++)
{
MethodCallExpression e;
if (isLinq)
e = Expression.Call(expr, methodInfo, new Expression[] { Expression.Constant(likeParts[i], typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase) });
else
e = Expression.Call(expr, methodInfo, Expression.Constant(likeParts[i], typeof(string)));
if (i == 0)
{
// e.IndexOf("likePart") > -1
q = q.Where(Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(e, Expression.Constant(-1, typeof(int))), eParam));
}
else
{
// e.IndexOf("likePart_previous")
MethodCallExpression ePrevious;
if (isLinq)
ePrevious = Expression.Call(expr, methodInfo, new Expression[] { Expression.Constant(likeParts[i - 1], typeof(string)), Expression.Constant(StringComparison.OrdinalIgnoreCase) });
else
ePrevious = Expression.Call(expr, methodInfo, Expression.Constant(likeParts[i - 1], typeof(string)));
// e.IndexOf("likePart_previous") < e.IndexOf("likePart")
q = q.Where(Expression.Lambda<Func<T, bool>>(Expression.LessThan(ePrevious, e), eParam));
}
}
return q;
}
Dato che non ha bisogno di SqlMethods, suppongo che tu possa usarlo per qualsiasi database, come MySql o Postgresql. Ma non lo so per certo. Ho provato questo con Sql Server usando Entity Framework 6. L'istruzione precedente genera il seguente codice in Sql Server.
SELECT [Extent1].* FROM SomeTable AS [Extent1]
WHERE ((( CAST(CHARINDEX(N'test if', [Extent1].[Name]) AS int)) - 1) > -1)
AND ((( CAST(CHARINDEX(N'test if', [Extent1].[Name]) AS int)) - 1) <
(( CAST(CHARINDEX(N'it work', [Extent1].[Name]) AS int)) - 1))
Per quanto riguarda le prestazioni, sembra che ci sia una discussione su ciò che è "migliore": LIKE o CHARINDEX. E da quello che ho letto CHARINDEX sembra essere il preferito.
.Where( column LIKE "Pattern")
var result = (from x in db.Members
where x.IDNumber.Contains(idnumber)
&& x.InstitutionIdentifier == institution.Identifier
select x).ToList();
return result;
Funzionerà sia per Linq che per SQL e Linq in memoria.
So che questo è un argomento vecchio, ma ecco la mia soluzione molto semplice:
string s=Regex.Escape("pattern - escaped for sanity").Replace("%", ".*").Replace("_", ".?");
user => Regex.IsMatch(user.FullName, s, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
In questo codice, sto usando caratteri di escape comuni per il linguaggio SQL.
Se vuoi usare dire *
e ?
, la stringa con escape conterrà \ *
e \?
, assicurati di includere il carattere barra rovesciata nelle istruzioni .Replace (...)
.
Ovviamente, se vuoi dare al tuo utente la possibilità di cercare RexEx, non sfuggire alla stringa di pattern.
Cerca nel tutorial Regex altre opzioni.
Credo che normalmente %
corrisponderà a almeno un carattere , mentre RegEx . *
corrisponderà a zero o più personaggi. Quindi, in realtà, il jolly %
è più simile a . +
(goloso) piuttosto che . *
(pigro).
Spero che questo aiuti.
non sono sicuro se parli LinqToSql o solo linq ... ma potresti espressioni regolari come questa:
.Where(dto => System.Text.RegularExpressions.Regex.IsMatch(dto.CustomerName, @"Ad"));
Nel codice .Net incluso LINQ to Objects, sto usando l'implementazione della funzione IsSqlLikeMatch dal thread Utilizzo di Regex per creare un " like " di SQL come funzione. .
Esempio di utilizzo
bool ret = message.IsSqlLikeMatch(pattern);
Maggiori dettagli nel mio post SQL " like " ; modelli da confrontare in .Net
Puoi anche utilizzare " contiene "
var myresult = db.MyItems.Where(x=>x.MyField.Contains(mysearchstring));
Stai parlando di LINQ a oggetti o LINQ a SQL?
Per LINQ agli oggetti dovrai ricorrere a espressioni regolari me pensa.