LINQ中的LIKE运算子


88

有没有办法像SQL一样比较C#LINQ表达式中的字符串 LIKE运算符?

假设我有一个字符串列表。在此列表中,我想搜索一个字符串。在SQL中,我可以这样写:

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

而不是上面的查询需要一个linq语法。

using System.Text.RegularExpressions;


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

我上面的LINQ语法不起作用。我怎么了?


1
当您将其放置到位时,此查询对我基本上是有效的。但是,我使用的是MongoDb Linq驱动程序,并且每个Linq提供程序在实现上都有差异...无论如何,谢谢。
Mark Ewer

这是我在LINQ中找到的最佳解决方案。谢谢。-@ Pranay-Rana
Abhishek Tomar

Answers:


140

通常,您使用String.StartsWith/ EndsWith/ Contains。例如:

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

我不知道是否有办法通过LINQ to SQL做正确的正则表达式。(请注意,它确实取决于您使用的提供程序-在LINQ to Objects中会很好;这取决于提供程序是否可以将调用转换为其本机查询格式,例如SQL。)

编辑:正如BitKFu所说的,Single当您期望得到一个准确的结果时应使用-当出现这种情况的错误时。的选项SingleOrDefaultFirstOrDefaultFirst应视使用究竟要的是什么。


朋友,但是,有一个问题,我的列表包含“ BALTIMORE”,而我给定的比较参数是“ BALTIMORE [MD],US”。以上语法无法选择。
shamim

2
看下面我的声明,它可能来自Single()方法。最好使用FirstOrDefault()
BitKFu 2011年

3
@shamim:所以您的数据中不包含您要查找的字符串?您希望它在SQL中如何工作?
乔恩·斯基特

在SQL中,可能没有结果集-在C#中,您将收到异常。这略有不同,而不是没有结果。这就是为什么我建议使用FirstOrDefault的原因。
BitKFu 2011年

@BitKFu从的起点开始Single()SingleOrDefault()这将是我的下一步,除非我们了解完整的上下文...
Marc Gravell

34

正则表达式?没有。但是对于该查询,您可以使用:

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

如果您确实需要SQL LIKE,则可以使用System.Data.Linq.SqlClient.SqlMethods.Like(...),LINQ-to-SQL映射到LIKESQL Server中。


@Maslow-恐怕不是我的专业领域,但我不相信有一种很好的干净方法可以将其映射到所有EF实现,所以...不。
Marc Gravell

2
这可能适用于SQL实现,但不适用于标准对象集合
Chris McGrath

13

好吧...有时可能会不舒服ContainsStartsWith或者EndsWith特别是在搜索值确定LIKE语句时,例如,开发人员传递的'value%'要求StartsWith在表达式中使用函数。所以我决定为IQueryable对象。

用法

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

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

您有适用的版本IEnumerable吗?
Nicke Manarin

8

如Jon Skeet和Marc Gravell所述,您可以简单地包含一个条件。但是对于类似的查询,采用Single()语句非常危险,因为这意味着您只能找到1个结果。如果获得更多结果,您将收到一个不错的例外:)

所以我更喜欢使用FirstOrDefault()而不是Single():

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

如果根据我们的预期,只有一场比赛,那么Single并非“危险”,而是“正确”。一切都取决于我们对数据的要求……“任何数字”,“至少一个”,“最多一个”,“恰好一个”等
Marc Gravell

3
取决于上下文,它可以是...完全取决于查询的期望
Marc Gravell

“空”或“%”搜索怎么样?可以处理“ B”,“ BALT”和“”(意思是让我得到一切)吗?
BlueChippy

8

在本机LINQ中,您可以使用Contains/StartsWith/EndsWith或RegExp的组合。

在LINQ2SQL中使用方法 SqlMethods.Like()

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

添加程序集:System.Data.Linq(在System.Data.Linq.dll中)以使用此功能。


我知道OP并没有实际说Linq2SQL,但这似乎是隐含的。我在这里的原因是StartsWith()Contains()等,千万不能用LINQ2SQL(工作至少我得到“的LINQ表达...无法翻译...”和“客户评价”,以使用ToList指令()哪位I”注意,在EF Core中,它已移至EF.Functions.Like()
Auspex

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

这就像SQL的“ LIKE”一样。


8
不..不,它不会像LIKE'term%'那样工作,这远不像整个like运算符一样工作,也不支持通配符
Chris McGrath


2

您可以使用谓词调用单个方法:

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

2

理想情况下,您应该使用StartWithEndWith

这是一个例子:

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

只需添加到字符串对象扩展方法即可。

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

用法:

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

考虑看一下如何写一个好的
赠品
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.