web-dev-qa-db-fra.com

LIKE opérateur dans LINQ

Existe-t-il un moyen de comparer les chaînes dans une expression C # LINQ similaire à l'opérateur LIKE de SQL?

Supposons que j'ai une liste de chaînes. Sur cette liste, je veux rechercher une chaîne. En SQL, je pourrais écrire:

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

Au lieu de ce qui précède, recherchez une syntaxe linq.

using System.Text.RegularExpressions;
…

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

Ma syntaxe LINQ ci-dessus ne fonctionne pas. Qu'est-ce que je me suis trompé?

82
shamim

En général, vous utilisez String.StartsWith/EndsWith/Contains. Par exemple:

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

Je ne sais pas s'il existe un moyen de faire les expressions régulières appropriées via LINQ to SQL cependant. (Notez que cela dépend vraiment du fournisseur que vous utilisez. Cela conviendrait dans LINQ to Objects; le problème est de savoir si le fournisseur peut convertir l'appel en son format de requête natif, par exemple SQL.)

EDIT: Comme le dit BitKFu, Single devrait être utilisé quand vous vous attendez à exactement un résultat - quand c'est une erreur de ne pas le faire. Les options de SingleOrDefault, FirstOrDefault ou First doivent être utilisées selon exactement ce qui est attendu.

130
Jon Skeet

Regex? non. Mais pour cette requête , vous pouvez simplement utiliser:

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

Si vous vraiment voulez SQL LIKE, vous pouvez utiliser System.Data.Linq.SqlClient.SqlMethods.Like(...) , que LINQ-to-SQL mappe à LIKE dans SQL Server.

30
Marc Gravell

Eh bien ... parfois, il peut être inconfortable d’utiliser Contains, StartsWith ou EndsWith surtout lors de la recherche de valeur, déterminer LIKE déclaration, par exemple. passé 'valeur%' oblige le développeur à utiliser la fonction StartsWith dans l'expression. J'ai donc décidé d'écrire l'extension pour les objets IQueryable.

Usage

// 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

Code

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;
    }
}
10
adobrzyc

Comme Jon Skeet et Marc Gravell ont déjà mentionné, vous pouvez simplement prendre une condition contient. Mais dans le cas d'une requête similaire, il est très dangereux de prendre une instruction Single (), car cela implique que vous ne trouviez qu'un résultat. Si vous obtenez plus de résultats, vous recevrez une exception Nice :)

Je préférerais donc utiliser FirstOrDefault () plutôt que Single ():

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

Dans LINQ natif, vous pouvez utiliser la combinaison de Contains/StartsWith/EndsWith ou de RegExp.

Dans LINQ2SQL, utilisez la méthode SqlMethods.Like()

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

ajoutez Assembly: System.Data.Linq (dans System.Data.Linq.dll) pour utiliser cette fonctionnalité.

7

Un simple comme ça

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

Résultat -> Annick, Yannick

3
Yannick Turbang

Vous pouvez appeler la méthode single avec un prédicat:

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

Idéalement, vous devriez utiliser StartWith ou EndWith.

Voici un exemple:

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

return lstPerson;
2
  .Where(e => e.Value.StartsWith("BALTIMORE"))

Cela fonctionne comme "J'aime" de SQL ...

2
user1930698
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();
0
Eber Camacho

J'ai trouvé une solution pour imiter l'opérateur SQL LIKE. Reportez-vous à la réponse que j'ai écrite ici https://stackoverflow.com/a/46592475/118607

0
Hugo
   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
NoBrend s

Il suffit d’ajouter aux méthodes d’extension d’objet chaîne.

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;
    }
}

usage:

use namespase that contains this class;

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