使用C#在EF 4.1查询中动态筛选列/属性

本文关键字:筛选 动态 属性 查询 EF 使用 | 更新日期: 2023-09-27 18:00:27

我正试图使用linq在C#中创建一个通用的"搜索引擎"。我有一个简单的搜索引擎,其功能和外观如下。

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 to Entities中不支持LINQ表达式节点类型"Invoke"。"系统。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专业知识的终点。有人有什么想法吗?

使用C#在EF 4.1查询中动态筛选列/属性

您应该使用谓词生成器来构造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 );

研究使用规范模式。查看此博客。具体来说,看看他开发的规范模式。这与@Variant的想法类似,在这里您可以构建一个动态规范,并将其传递给您的上下文或存储库。

结果发现查询的内容只需要"扩展"即可。我使用了在这里找到的一个库来扩展表达式。我认为这允许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);