Как оператор или использование подстановочных знаков в LINQ to Entities - PullRequest
8 голосов
/ 02 февраля 2011

Я использую сущности 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 ]

16 голосов
/ 23 апреля 2013

Метод SQL PATINDEX обеспечивает те же функции, что и LIKE.Поэтому вы можете использовать метод SqlFunctions.PatIndex :

.Where(x => SqlFunctions.PatIndex("%test%.doc%", x.MySearchField) > 0)
3 голосов
/ 15 ноября 2012

Исходя из правильного ответа Магнуса, вот метод расширения, который можно использовать повторно, как мне было необходимо в моем проекте.

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 есть пример , с которого вы могли бы начать, если бы вместо этого вы хотели расширение iQueryable.

2 голосов
/ 03 февраля 2011

Split the String

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));
}
2 голосов
/ 02 февраля 2011

Используйте регулярное выражение ...

Далее будут распечатаны все файлы в текущем каталоге, которые соответствуют 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);
            }
        }
    }
}
2 голосов
/ 02 февраля 2011

Вы можете попробовать использовать в этой статье , где автор описывает, как построить оператор LIKE с подстановочными символами в LINQ to Entities.

РЕДАКТИРОВАТЬ: Так как исходная ссылка теперь мертва, вот исходный класс расширения (согласно Джон Кетер в комментариях ) и пример использования.

Расширение:

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, '*');
0 голосов
/ 23 мая 2013

Таким образом, я пытался сделать то же самое - пытаться создать парные списки, чтобы вернуть всех кандидатов, которые соответствуют SearchTerm.Я хотел, чтобы, если пользователь набрал «Аризона», он возвращал все, независимо от случая, в котором была Аризона.Кроме того, если пользователь введет «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;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...