【问题标题】:Like operator or using wildcards in LINQ to EntitiesLike 运算符或在 LINQ to Entities 中使用通配符
【发布时间】:2011-02-02 19:00:25
【问题描述】:

我正在使用 LINQ 2 实体。 以下是问题:

string str = '%test%.doc%' 
.Contains(str) // converts this into LIKE '%~%test~%.doc~%%'

预期转换:LIKE '%test%.doc%'

如果是 LINQ 2 SQL,我可以使用 SqlMethods.Like,因为有人在我之前的问题中回答了它。但是现在我使用的是 L2E 而不是 L2S,我需要其他解决方案。

【问题讨论】:

  • 现在我动态创建了一个 sql 字符串并通过 ExecuteStoreQuery 执行它并解决了我的问题。

标签: c# linq linq-to-entities


【解决方案1】:

SQL 方法 PATINDEX 提供与 LIKE 相同的功能。因此,您可以使用SqlFunctions.PatIndex 方法:

.Where(x => SqlFunctions.PatIndex("%test%.doc%", x.MySearchField) > 0)

【讨论】:

  • 工作就像一个魅力。谢谢BG!
  • 注意:你需要“使用 System.Data.Entity.SqlServer;”不是“使用 System.Data.Objects.SqlClient;”见stackoverflow.com/questions/19733085/…
  • 太棒了!是的,LIKE == PATINDEX > 0docs.microsoft.com/en-us/sql/t-sql/functions/… 人们真的需要支持这个答案,因为几乎每个帖子都有大量被接受的错误答案(就像这篇帖子一样),而且很难弄清楚这是这样做的方法。跨度>
  • @jgerman 这将是理想的解决方案。但是,三年后的现在,我认为 System.Data.Entity.SqlServer 在 Entity Framework Core 1.1 中不可用。 ://
  • PATINDEX 也比 LIKE 慢很多
【解决方案2】:

根据 Magnus 的正确答案,这是一个可以重复使用的扩展方法,正如我在项目中需要的那样。

public static class LinqExtensions
{
    public static Expression<Func<T, bool>> WildCardWhere<T>(this Expression<Func<T, bool>> source, Expression<Func<T, string>> selector, string terms, char separator)
    {
        if (terms == null || selector == null)
            return source;

        foreach (string term in terms.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries))
        {
            string current = term;
            source = source.And(
                Expression.Lambda<Func<T, bool>>(
                    Expression.Call(selector.Body, "Contains", null, Expression.Constant(current)),
                    selector.Parameters[0]
                )
            );
        }

        return source;
    }
}

用法:

var terms = "%test%.doc%";
Expression<Func<Doc, bool>> whereClause = d => d;
whereClause = whereClause.WildCardWhere(d => d.docName, terms, '%');
whereClause = whereClause.WildCardWhere(d => d.someOtherProperty, "another%string%of%terms", '%');
var result = ListOfDocs.Where(whereClause).ToList();

扩展使用http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/ 的谓词构建器。生成的 sql 对表进行单表扫描,无论其中有多少项。 Jo Vdb 有一个example,如果你想要 iQueryable 的扩展,你可以从它开始。

【讨论】:

  • 感谢@Legolomaniac,这是一个非常棒的解决方案,但我无法构建它...特别是在`source = source.And(`它告诉我,"方法 'And' 没有重载需要 1 个参数"
【解决方案3】:

您可以尝试使用this article,其中作者描述了如何在 LINQ to Entities 中使用通配符构建 LIKE 语句。

编辑:由于原始链接现已失效,这里是原始扩展类(根据Jon Koeter in the comments)和用法示例。

扩展:

public static class LinqHelper
{
    //Support IQueryable (Linq to Entities)
    public static IQueryable<TSource> WhereLike<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, string>> valueSelector, string value, char wildcard)
    {
        return source.Where(BuildLikeExpression(valueSelector, value, wildcard));
    }

    //Support IEnumerable (Linq to objects)
    public static IEnumerable<TSource> WhereLike<TSource>(this IEnumerable<TSource> sequence, Func<TSource, string> expression, string value, char wildcard)
    {
        var regEx = WildcardToRegex(value, wildcard);

        //Prevent multiple enumeration:
        var arraySequence = sequence as TSource[] ?? sequence.ToArray();

        try
        {
            return arraySequence.Where(item => Regex.IsMatch(expression(item), regEx));
        }
        catch (ArgumentNullException)
        {
            return arraySequence;
        }
    }

    //Used for the IEnumerable support
    private static string WildcardToRegex(string value, char wildcard)
    {
        return "(?i:^" + Regex.Escape(value).Replace("\\" + wildcard, "." + wildcard) + "$)";
    }

    //Used for the IQueryable support
    private static Expression<Func<TElement, bool>> BuildLikeExpression<TElement>(Expression<Func<TElement, string>> valueSelector, string value, char wildcard)
    {
        if (valueSelector == null) throw new ArgumentNullException("valueSelector");

        var method = GetLikeMethod(value, wildcard);

        value = value.Trim(wildcard);
        var body = Expression.Call(valueSelector.Body, method, Expression.Constant(value));

        var parameter = valueSelector.Parameters.Single();
        return Expression.Lambda<Func<TElement, bool>>(body, parameter);
    }

    private static MethodInfo GetLikeMethod(string value, char wildcard)
    {
        var methodName = "Equals";

        var textLength = value.Length;
        value = value.TrimEnd(wildcard);
        if (textLength > value.Length)
        {
            methodName = "StartsWith";
            textLength = value.Length;
        }

        value = value.TrimStart(wildcard);
        if (textLength > value.Length)
        {
            methodName = (methodName == "StartsWith") ? "Contains" : "EndsWith";
        }

        var stringType = typeof(string);
        return stringType.GetMethod(methodName, new[] { stringType });
    }
}

用法示例:

string strEmailToFind = "%@yahoo.com"

IQueryable<User> myUsers = entities.Users.WhereLike(u => u.EmailAddress, strEmailToFind, '%');

或者,如果您希望您的用户更习惯于 Windows Explorer 样式的通配符:

string strEmailToFind = "*@yahoo.com"

IQueryable<User> myUsers = entities.Users.WhereLike(u => u.EmailAddress, strEmailToFind, '*');

【讨论】:

  • 前面看到了,没有例子可以使用
  • 这里是一个例子:entities.Table.WhereLike(el => el.position, position, '%')。因此,您应该设置要搜索的列、要搜索的模式和通配符。但是我已经对其进行了测试,发现它对您没有帮助 - 它不会处理不在模式开头或结尾的通配符,抱歉。
  • 但是如何组合多个 Where 子句?例如,要生成“WHERE FirstName LIKE 'A%' OR LastName LIKE 'A%'”?
  • “这篇文章”链接trentacular.com/2010/08/… 是“未找到”...trentacular.com 显示“trentacular.com 快到了!上传您的网站以开始使用。”
  • 不幸的是,链接已失效。这就是为什么应避免仅链接答案的原因
【解决方案4】:

使用正则表达式...

以下将打印出当前目录中与 test.doc* 匹配的所有文件(dos 通配符样式 - 我相信这是您所要求的)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace RegexFileTester
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] _files = Directory.GetFiles(".");
            var _fileMatches =  from i in _files
                                where Regex.IsMatch(i, ".*test*.doc.*")
                                //where Regex.IsMatch(i, ".*cs")
                                select i;
            foreach(var _file in _fileMatches)
            {
                Console.WriteLine(_file);
            }
        }
    }
}

【讨论】:

  • 不,这不是必需的。您正在获取所有文件,然后过滤这是不必要的数据传输。
  • 有什么选择?我刚刚在我的代码中做到了这一点,如何同时选择和查询文件名?
  • 使用 Directory.EnumerateFiles(path, searchPattern) 代替。 (但我认为他想查询数据库)
【解决方案5】:

拆分字符串

var str =  "%test%.doc%";
var arr = str.Split(new[]{'%'} ,StringSplitOptions.RemoveEmptyEntries);
var q = tblUsers.Select (u => u);
foreach (var item in arr)
{
    var localItem = item;
    q = q.Where (x => x.userName.Contains(localItem));
}

【讨论】:

    【解决方案6】:

    所以我尝试了同样的事情 - 尝试将列表配对以返回与 SearchTerm 匹配的所有候选人。我想要它,以便如果用户键入“Arizona”,它会返回所有内容,无论是否有 Arizona。此外,如果用户键入“Arizona Cha”,它将返回“Arizona License Change”之类的项目。以下工作:

    private List<Certification> GetCertListBySearchString()
        {
            string[] searchTerms = SearchString.Split(' ');
            List<Certification> allCerts = _context.Certifications.ToList();
    
            allCerts = searchTerms.Aggregate(allCerts, (current, thisSearchString) => (from ac in current
                                                                                       where ac.Name.ToUpper().Contains(thisSearchString.ToUpper())
                                                                                       select ac).ToList());
              return allCerts;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-09
      • 2011-08-05
      • 2015-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多