Operatore LIKE in LINQ


88

Esiste un modo per confrontare le stringhe in un'espressione LINQ C # simile all'operatore di SQL LIKE?

Supponiamo che io abbia una lista di stringhe. In questo elenco voglio cercare una stringa. In SQL potrei scrivere:

SELECT * FROM DischargePort WHERE PortName LIKE '%BALTIMORE%'

Invece di quanto sopra, la query vuole una sintassi linq.

using System.Text.RegularExpressions;


var regex = new Regex(sDischargePort, RegexOptions.IgnoreCase);
var sPortCode = Database.DischargePorts
                .Where(p => regex.IsMatch(p.PortName))
                .Single().PortCode;

La mia sintassi LINQ sopra non funziona. Cosa ho sbagliato?


1
Questa query essenzialmente ha funzionato per me mentre la metti in atto. Ma sto usando il driver MongoDb Linq e ci sono differenze di implementazione in ogni provider Linq ... comunque, grazie.
Mark Ewer

Questa è la migliore soluzione che ho trovato per come in LINQ. Grazie. - @ Pranay-Rana
Abhishek Tomar

Risposte:


140

In genere usi String.StartsWith/ EndsWith/ Contains. Per esempio:

var portCode = Database.DischargePorts
                       .Where(p => p.PortName.Contains("BALTIMORE"))
                       .Single()
                       .PortCode;

Non so se esiste un modo per eseguire espressioni regolari corrette tramite LINQ to SQL. (Tieni presente che dipende davvero dal provider che stai utilizzando: andrebbe bene in LINQ to Objects; è una questione che il provider possa convertire la chiamata nel suo formato di query nativo, ad esempio SQL.)

EDIT: come dice BitKFu, Singledovrebbe essere usato quando ti aspetti esattamente un risultato - quando è un errore perché non sia così. Opzioni di SingleOrDefault, FirstOrDefaulto Firstdovrebbero essere utilizzati a seconda esattamente quello che ci si aspetta.


amico ma c'è un problema, la mia lista contiene "BALTIMORE" e il mio parametro di confronto dato è "BALTIMORE [MD], US". La sintassi sopra non riesce a selezionare.
shamim

2
dai un'occhiata alla mia dichiarazione qui sotto, potrebbe provenire dal metodo Single (). È meglio usare FirstOrDefault ()
BitKFu

3
@shamim: Quindi i tuoi dati non contengono la stringa che stai cercando? Come ti aspetteresti che funzioni anche in SQL?
Jon Skeet

In SQL potresti non ottenere alcun set di risultati: in C # riceverai un'eccezione. Che è leggermente diverso, invece di nessun risultato. Ecco perché ho consigliato di utilizzare FirstOrDefault.
BitKFu

@BitKFu da un punto di partenza di Single(), SingleOrDefault()sarebbe il mio prossimo passo, a meno che non comprendiamo il contesto completo ...
Marc Gravell

34

Regex? no. Ma per quella query puoi semplicemente usare:

 string filter = "BALTIMORE";
 (blah) .Where(row => row.PortName.Contains(filter)) (blah)

Se si desidera veramente SQL LIKE, è possibile utilizzare il System.Data.Linq.SqlClient.SqlMethods.Like(...)mapping a cui LINQ-to-SQL è associato LIKEin SQL Server.


@ Maslow - non è la mia area di competenza, temo - ma non credo che esista un modo pulito e carino per mapparlo a tutte le implementazioni EF, quindi ... no.
Marc Gravell

2
questo può funzionare su implementazioni SQL ma non funziona con una raccolta di oggetti standard
Chris McGrath

13

Bene ... a volte può essere scomodo da usare Contains, StartsWitho EndsWithspecialmente quando si cerca il valore, determinare la LIKEdichiarazione, ad esempio il 'valore%' passato richiede allo sviluppatore di usare la StartsWithfunzione nell'espressione. Quindi ho deciso di scrivere l'estensione per gli IQueryableoggetti.

Utilizzo

// numbers: 11-000-00, 00-111-00, 00-000-11

var data1 = parts.Like(p => p.Number, "%11%");
// result: 11-000-00, 00-111-00, 00-000-11

var data2 = parts.Like(p => p.Number, "11%");
// result: 11-000-00

var data3 = parts.Like(p => p.Number, "%11");
// result: 00-000-11

Codice

public static class LinqEx
{
    private static readonly MethodInfo ContainsMethod = typeof(string).GetMethod("Contains");
    private static readonly MethodInfo StartsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
    private static readonly MethodInfo EndsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });

    public static Expression<Func<TSource, bool>> LikeExpression<TSource, TMember>(Expression<Func<TSource, TMember>> property, string value)
    {
        var param = Expression.Parameter(typeof(TSource), "t");
        var propertyInfo = GetPropertyInfo(property);
        var member = Expression.Property(param, propertyInfo.Name);

        var startWith = value.StartsWith("%");
        var endsWith = value.EndsWith("%");

        if (startWith)
            value = value.Remove(0, 1);

        if (endsWith)
            value = value.Remove(value.Length - 1, 1);

        var constant = Expression.Constant(value);
        Expression exp;

        if (endsWith && startWith)
        {
            exp = Expression.Call(member, ContainsMethod, constant);
        }
        else if (startWith) 
        {
            exp = Expression.Call(member, EndsWithMethod, constant);
        }
        else if (endsWith)
        {
            exp = Expression.Call(member, StartsWithMethod, constant);
        }
        else
        {
            exp = Expression.Equal(member, constant);
        }

        return Expression.Lambda<Func<TSource, bool>>(exp, param);
    }

    public static IQueryable<TSource> Like<TSource, TMember>(this IQueryable<TSource> source, Expression<Func<TSource, TMember>> parameter, string value)
    {
        return source.Where(LikeExpression(parameter, value));
    }

    private static PropertyInfo GetPropertyInfo(Expression expression)
    {
        var lambda = expression as LambdaExpression;
        if (lambda == null)
            throw new ArgumentNullException("expression");

        MemberExpression memberExpr = null;

        switch (lambda.Body.NodeType)
        {
            case ExpressionType.Convert:
                memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
                break;
            case ExpressionType.MemberAccess:
                memberExpr = lambda.Body as MemberExpression;
                break;
        }

        if (memberExpr == null)
            throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");


        var output = memberExpr.Member as PropertyInfo;

        if (output == null)
            throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");

        return output;
    }
}

Hai una versione che funziona con IEnumerable?
Nicke Manarin

8

Come già menzionato da Jon Skeet e Marc Gravell, puoi semplicemente prendere una condizione contiene. Ma nel caso della tua query like, è molto pericoloso prendere un'istruzione Single (), perché ciò implica che trovi solo 1 risultato. In caso di più risultati, riceverai una bella eccezione :)

Quindi preferirei usare FirstOrDefault () invece di Single ():

var first = Database.DischargePorts.FirstOrDefault(p => p.PortName.Contains("BALTIMORE"));
var portcode = first != null ? first.PortCode : string.Empty;

se è nostra presunta aspettativa che ci sia esattamente una corrispondenza, Single non è "pericoloso" - è "corretto". Tutto si riduce a ciò che affermiamo sui dati ... "qualsiasi numero", "almeno uno", "al massimo uno", "esattamente uno", ecc.
Marc Gravell

3
a seconda del contesto, può essere ... dipende interamente dall'aspettativa della query
Marc Gravell

Che ne dici di una ricerca "vuota" o "%"? Questo potrebbe gestire "B", "BALT" e "" (che significa farmi avere tutto)?
BlueChippy

8

In LINQ nativo è possibile utilizzare una combinazione di Contains/StartsWith/EndsWitho RegExp.

In LINQ2SQL utilizzare il metodo SqlMethods.Like()

    from i in db.myTable
    where SqlMethods.Like(i.field, "tra%ata")
    select i

aggiungere Assembly: System.Data.Linq (in System.Data.Linq.dll) per utilizzare questa funzionalità.


Capisco che l'OP non abbia effettivamente detto Linq2SQL, ma sembrava implicito. Il motivo per cui sono qui è che StartsWith(), Contains()ecc, fanno non lavoro con Linq2SQL (almeno io ottenere "L'espressione LINQ ... non poteva essere tradotto ..." e un'istruzione per l'uso ToList () per "la valutazione cliente" -che io' . m già facendo si noti, in EF core, viene spostato aEF.Functions.Like()
Auspex

3
  .Where(e => e.Value.StartsWith("BALTIMORE"))

Funziona come "LIKE" di SQL ...


8
no .. no, non funziona solo come il 'termine%' MI PIACE che è ben lungi dal funzionare come l'operatore simile nel suo insieme e non supporta i caratteri jolly
Chris McGrath,

3

Un semplice come questo

string[] users = new string[] {"Paul","Steve","Annick","Yannick"};    
var result = from u in users where u.Contains("nn") select u;

Risultato -> Annick, Yannick


2

Puoi chiamare il metodo singolo con un predicato:

var portCode = Database.DischargePorts
                   .Single(p => p.PortName.Contains("BALTIMORE"))
                   .PortCode;

2

Idealmente dovresti usare StartWithoEndWith .

Ecco un esempio:

DataContext  dc = new DCGeneral();
List<Person> lstPerson= dc.GetTable<Person>().StartWith(c=> c.strNombre).ToList();

return lstPerson;

0
   public static class StringEx
    {
        public static bool Contains(this String str, string[] Arr, StringComparison comp)
        {
            if (Arr != null)
            {
                foreach (string s in Arr)
                {
                    if (str.IndexOf(s, comp)>=0)
                    { return true; }
                }
            }

            return false;
        }

        public static bool Contains(this String str,string[] Arr)
        {
            if (Arr != null)
            {
                foreach (string s in Arr)
                {
                    if (str.Contains(s))
                    { return true; }
                }
            }

            return false;
        }
    }


var portCode = Database.DischargePorts
                   .Single(p => p.PortName.Contains( new string[] {"BALTIMORE"},  StringComparison.CurrentCultureIgnoreCase) ))
                   .PortCode;

0

Basta aggiungere ai metodi di estensione degli oggetti stringa.

public static class StringEx
{
    public static bool Contains(this String str, string[] Arr, StringComparison comp)
    {
        if (Arr != null)
        {
            foreach (string s in Arr)
            {
                if (str.IndexOf(s, comp)>=0)
                { return true; }
            }
        }

        return false;
    }

    public static bool Contains(this String str,string[] Arr)
    {
        if (Arr != null)
        {
            foreach (string s in Arr)
            {
                if (str.Contains(s))
                { return true; }
            }
        }

        return false;
    }
}

utilizzo:

use namespase that contains this class;

var sPortCode = Database.DischargePorts
            .Where(p => p.PortName.Contains(new string [] {"BALTIMORE"},  StringComparison.CurrentCultureIgnoreCase) )
            .Single().PortCode;

0
List<Categories> categoriess;
        private void Buscar()
        {
            try
            {
                categoriess = Contexto.Categories.ToList();
                categoriess = categoriess.Where(n => n.CategoryID >= Convert.ToInt32(txtCatID.Text) && n.CategoryID <= Convert.ToInt32(txtCatID1.Text) && (n.CategoryName.Contains(txtCatName.Text)) ).ToList();

considera di dare un'occhiata a come scrivere una buona
risposta

0

@adobrzyc aveva questa fantastica LIKEfunzione personalizzata : volevo solo condividerne la IEnumerableversione.

public static class LinqEx
{
    private static readonly MethodInfo ContainsMethod = typeof(string).GetMethod("Contains");
    private static readonly MethodInfo StartsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
    private static readonly MethodInfo EndsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });

    private static Func<TSource, bool> LikeExpression<TSource, TMember>(Expression<Func<TSource, TMember>> property, string value)
    {
        var param = Expression.Parameter(typeof(TSource), "t");
        var propertyInfo = GetPropertyInfo(property);
        var member = Expression.Property(param, propertyInfo.Name);

        var startWith = value.StartsWith("%");
        var endsWith = value.EndsWith("%");

        if (startWith)
            value = value.Remove(0, 1);

        if (endsWith)
            value = value.Remove(value.Length - 1, 1);

        var constant = Expression.Constant(value);
        Expression exp;

        if (endsWith && startWith)
        {
            exp = Expression.Call(member, ContainsMethod, constant);
        }
        else if (startWith)
        {
            exp = Expression.Call(member, EndsWithMethod, constant);
        }
        else if (endsWith)
        {
            exp = Expression.Call(member, StartsWithMethod, constant);
        }
        else
        {
            exp = Expression.Equal(member, constant);
        }

        return Expression.Lambda<Func<TSource, bool>>(exp, param).Compile();
    }

    public static IEnumerable<TSource> Like<TSource, TMember>(this IEnumerable<TSource> source, Expression<Func<TSource, TMember>> parameter, string value)
    {
        return source.Where(LikeExpression(parameter, value));
    }


    private static PropertyInfo GetPropertyInfo(Expression expression)
    {
        var lambda = expression as LambdaExpression;
        if (lambda == null)
            throw new ArgumentNullException("expression");

        MemberExpression memberExpr = null;

        switch (lambda.Body.NodeType)
        {
            case ExpressionType.Convert:
                memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
                break;
            case ExpressionType.MemberAccess:
                memberExpr = lambda.Body as MemberExpression;
                break;
        }

        if (memberExpr == null)
            throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");


        var output = memberExpr.Member as PropertyInfo;

        if (output == null)
            throw new InvalidOperationException("Specified expression is invalid. Unable to determine property info from expression.");

        return output;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.