Динамическая фильтрация столбца / свойства в запросе EF 4.1 с использованием C # - PullRequest
2 голосов
/ 28 июня 2011

Я пытаюсь создать универсальный «поисковый движок» в C #, используя linq.У меня есть простая поисковая система, которая работает и выглядит следующим образом:

var query = "joh smi";
var searchTerms = query.Split(new char[] { ' ' });
var numberOfTerms = searchTerms.Length;

var matches = from p in this.context.People
              from t in searchTerms
              where p.FirstName.Contains(t) ||
              p.LastName.Contains(t)
              group p by p into g
              where g.Count() == numberOfTerms
              select g.Key;

Я хочу, чтобы она была более общей, поэтому я могу назвать ее так:

var matches = Search<Person>(dataset, query, p => p.FirstName, p => p.LastName);

У меня естьполучено следующее, но он завершается с ошибкой «Тип узла выражения LINQ 'Invoke' не поддерживается в LINQ to Entities."System.NotSupportedException.

static IEnumerable<T> Find<T>(IQueryable<T> items, string query,
                              params Func<T, string>[] properties)
{
    var terms = query.Split(' ');
    var numberOfParts = terms.Length;

    foreach (var prop in properties)
    {
        var transformed = items.SelectMany(item => terms, 
                (item, term) => new { term, item });

                                              // crashes due to this method call
        var filtered = transformed.Where(p => prop(p.item).Contains(p.term));
        items = filtered.Select(p => p.item);
    }

    return from i in items
           group i by i into g
           where g.Count() == numberOfParts
           select g.Key;
}

Я уверен, что это выполнимо, просто должен быть способ скомпилировать i => i.FirstName в Expression<Func<T, bool>>, но на этом мой опыт LINQ заканчивается.У кого-нибудь есть идеи?

Ответы [ 3 ]

1 голос
/ 28 июня 2011

Вы должны использовать Predicate Builder для создания запроса Or, например:

var predicate = PredicateBuilder.False<T>();
foreach (var prop in properties)
{
    Func<T, string> currentProp = prop;
    predicate = predicate.Or (p => currentProp(p.item).Contains(p.term));
}
var result = items.Where(predicate );
0 голосов
/ 01 июля 2011

Получается, что содержание запросов просто необходимо было «расширить».Я использовал библиотеку, которую нашел здесь , чтобы расширить выражения.Я думаю, что позволяет Linq to Entities перевести его в sql.Вы заметите, что Expand вызывают снова и снова;Я думаю, что все они необходимы.Это работает, в любом случае.Код для следования:

using System.Linq.Expressions;

public static class SearchEngine<T>
{
    class IandT<T>
    {
        public string Term { get; set; }
        public T Item { get; set; }
    }

    public static IEnumerable<T> Find(
                  IQueryable<T> items,
                  string query,
                  params Expression<Func<T, string>>[] properties)
    {
        var terms = query.Split(new char[] { ' ' },
                                StringSplitOptions.RemoveEmptyEntries);
        var numberOfParts = terms.Length;

        Expression<Func<IandT<T>, bool>> falseCond = a => false;
        Func<Expression<Func<IandT<T>, bool>>,
             Expression<Func<IandT<T>, bool>>,
             Expression<Func<IandT<T>, bool>>> combineOr = 
                (f, g) => (b) => f.Expand(b) || g.Expand(b);

        var criteria = falseCond;

        foreach (var prop in properties)
        {
            var currentprop = prop;
            Expression<Func<IandT<T>, bool>> current = c => 
                    currentprop.Expand(c.Item).IndexOf(c.Term) != -1;
            criteria = combineOr(criteria.Expand(), current.Expand());
        }

        return from p in items.ToExpandable()
               from t in terms
               where criteria.Expand(new IandT<T> { Item = p, Term = t })
               group p by p into g
               where g.Count() == numberOfParts
               select g.Key;
    }
}

Его можно вызвать с помощью следующего кода:

var matches = Search<Person>(dataset, query, p => p.FirstName, p => p.LastName);
0 голосов
/ 28 июня 2011

Изучите использование шаблона спецификации. Проверьте этот блог . В частности, посмотрите на спецификацию, которую он разработал. Это похоже на @Variant, где вы можете создать динамическую спецификацию и передать ее в свой контекст или хранилище.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...