像LINQ to Entities中的运算符或使用通配符一样

时间:2011-02-02 19:00:26

标签: c# linq linq-to-entities

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

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

预期转化:LIKE'%test%。doc%'

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

6 个答案:

答案 0 :(得分:16)

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

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

答案 1 :(得分:3)

继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对表执行单个表扫描,无论其中有多少个术语。如果你想要iQueryable的扩展,你可以从Jo Vdb开始example

答案 2 :(得分:2)

您可以尝试使用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资源管理器风格的通配符:

string strEmailToFind = "*@yahoo.com"

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

答案 3 :(得分:2)

使用正则表达式...

以下将打印出当前目录中与 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);
            }
        }
    }
}

答案 4 :(得分:2)

拆分字符串

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

答案 5 :(得分:0)

所以我尝试了同样的事情 - 尝试配对List以返回匹配SearchTerm的所有候选者。我想要它,以便用户输入&#34; Arizona&#34;无论亚利桑那州的情况如何,它都会归还一切。此外,如果用户键入&#34; Arizona Cha&#34;,它将返回类似&#34; Arizona License Change&#34;等项目。以下工作:

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