2009-06-24 16 views
29

Vorrei sapere se è possibile eseguire una ricerca con caratteri jolly utilizzando LINQ.Ricerca con caratteri jolly per LINQ

vedo LINQ ha Contiene, StartsWith, EndsWith, ecc

Cosa succede se voglio qualcosa come% di prova se% funzionare%, come posso farlo?

saluti

risposta

28

Vorrei utilizzare le espressioni regolari, poiché non si potrebbe sempre utilizzare Linq in 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); 
} 
+6

Bit a vuoto - potrebbe semplicemente usare SqlMethods.Like –

+1

Si presume sempre che si stia chiamando un database SQL. Cosa succede se si sta interrogando un elenco che è stato creato in memoria? –

+9

La sua domanda è contrassegnata con SQL –

69

È possibile utilizzare SqlMethods.Like().

Un esempio di utilizzo:

var results = 
     from u in users 
     where SqlMethods.Like(u.FirstName, "%John%") 
     select u; 
+2

molto cool! non sapevo che esistesse ... – bytebender

+11

Funzionerà solo con le query di LinqToSql (come dovrebbe essere evidente dalla classe utilizzata). –

+0

Molto bello, non sapevo che esistesse sia +1 –

2
.Where(column LIKE "Pattern") 
+3

Bene per VB, ma non C#. – Noldorin

0

Stai parlando LINQ to oggetti o LINQ to SQL?

Per LINQ agli oggetti, è necessario ricorrere al numero regular expressions.

1

non è sicuro se si parla LinqToSql o solo LINQ ... ma si potrebbe espressioni regolari come questo:

.Where(dto => System.Text.RegularExpressions.Regex.IsMatch(dto.CustomerName, @"Ad")); 
12

aggiungi System.Data.Linq.SqlClient al vostro usando o l'elenco delle importazioni quindi provare:

var results= from x in data 
      where SqlMethods.Like(x.SearchField, “%something%like%this%”) 
      select x; 
3
var result = (from x in db.Members 
       where x.IDNumber.Contains(idnumber) 
       && x.InstitutionIdentifier == institution.Identifier 
       select x).ToList(); 
return result; 

lavorerà sia per LINQ to SQL e LINQ in memoria.

2

È inoltre possibile utilizzare "contiene"

var myresult = db.MyItems.Where(x=>x.MyField.Contains(mysearchstring)); 
2

So che questo è e argomento vecchio, ma qui è 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 utilizzando i caratteri di escape comuni per il linguaggio SQL. Se si desidera utilizzare dire * e ?, sfuggito stringa conterrà \* e \? corrispondentemente, assicurarsi di includere il backslash carattere nel .Replace(...) statement (s). Ovviamente, se vuoi dare la possibilità all'utente di cercare RexEx, non sfuggire alla stringa del pattern.

Esercitazione di ricerca Regex per altre opzioni.

credo normalmente % corrisponderà almeno un carattere, mentre il RegEx .* corrisponderà zero o più caratteri. Quindi, in realtà, il carattere jolly % è più simile a .+ (goloso) anziché a .* (pigro).

Spero che questo aiuti.

2

Guardando la questione

Cosa succede se voglio qualcosa come% di prova se% funzionare%, come posso farlo?

quindi mi aspetto qualcosa di

LIKE '%Test if%it work%' 

il che significa che la stringa deve contenere 'Test se' e 'Funzionerà', in questo ordine.

Questo non funziona:

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(); 

poi troverò tutti i record che contengono sia "Test se" e "funziona", ma non nello specifico nell'ordine.

Quindi con Contiene questo non è possibile. Ma con IndexOf lo è.

IndexOf individuerà la stringa di ricerca E restituirà la posizione di essa nella stringa. Permettendo di trovare le parole nell'ordine corretto.

- Aggiornamento -

Con mia risposta iniziale non era il mio obiettivo di fornire una soluzione generica, ma piuttosto un esempio di un altro approccio che non è dependend sql. Quindi è corretto che l'esempio originale risponda solo alla domanda letterale. Ma dal momento che la risposta potrebbe 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 "Test se" e "Funziona", in questo ordine.

context.SomeTable.Like("test if%it work", "Name").ToList(); 

listOfString.Like("test if%it work").ToList(); 

estensione, consente a 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; 
} 

Dal momento che non ha bisogno SqlMethods presumo si può usare questo per qualsiasi database, come MySQL o PostgreSQL. Ma non lo so per certo. Ho provato questo con SQL Server utilizzando 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)) 

A proposito di prestazioni, sembra che ci sia un po 'di discussione su ciò che è 'meglio': LIKE o CHARINDEX.E da quello che ho letto, CHARINDEX sembra essere il preferito.

+0

La risposta non contiene una risposta alla domanda generale su come implementare la ricerca con caratteri jolly in LINQ, risponde solo all'esempio specifico e non funzionerà per nessun'altra circostanza. In quanto tale, non fornisce una risposta utile alla domanda. – Frosty840

+0

@ Frosty840 Ho aggiornato la risposta. –

+0

In realtà stavo cercando una risposta specifica SQL, in quanto mi infastidisce dover scrivere un codice generico extra che non avrò mai bisogno visto che useremo sempre SQL Server. Sfortunatamente, mentre hai mostrato perché contiene non funzionerà, non hai mostrato nulla di così semplice. Il confronto che sto cercando di fare è uno di una stringa di contiene. Quindi speravo in qualcosa di semplice da aggiungere a quella stringa. Sembra SQLMethods.Like dovrà essere utilizzato. – user1161391

0

Per Entity Framework Core 2.0 c'è LIKE operatore (announced in August 2017):

var query = from e in _context.Employees 
        where EF.Functions.Like(e.Title, "%developer%") 
        select e; 
Problemi correlati