我正在尝试使用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'。” 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专业知识结束的地方。有没有人有任何想法?
答案 0 :(得分:1)
您应该使用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 );
答案 1 :(得分:0)
使用规范模式。查看this blog。具体来说,看看他开发的spec模式。这与@Variant类似,您可以在其中构建动态规范并将其传递给您的上下文或存储库。
答案 2 :(得分:0)
事实证明,查询的内容只需要扩展&#39;。我使用了一个我找到here的库来扩展表达式。我认为这允许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);