我正在为网站构建一个简单的搜索功能。目的是让用户找到“游泳者”并通过搜索框将其添加到列表中,使用“bob staff”之类的查询。
我决定解决的第一部分是允许用户通过组名搜索(在数据库中,所有游泳者都是组的一部分)。以下是我目前的代码。
[HttpGet]
public JsonResult searchForSwimmers(string q)
{
//designed to return [{name='foo',id='bar'}]
String[] QueryTerms = q.Split(' '); //all the search terms are sep. by " "
var groupResults = _db.SwimGroups.Where(g => g.Name.ContainsAny(QueryTerms))
.OrderByDescending(g => g.Name.StartsWithAny(QueryTerms) ? 1 : 0)
.ThenBy( g => g)
.Select(g => new { name = g.Name, id = g.ID });
return Json(groupResults,JsonRequestBehavior.AllowGet);
}
在第8行,有一个名为 StartsWithAny 的方法被调用。这是我在以下文件中定义的扩展方法:
public static class StringUtils
{
public static Boolean StartsWithAny(this String str, params String[] Fragments)
{
foreach (String fragment in Fragments)
{
if (str.StartsWith(fragment))
{
return true;
}
}
return false;
}
}
这个想法是,如果名称以其中一个术语开头,那么它应该在相关性中排名更高。我认识到这种逻辑是天真的,并且有缺陷,但我认为这将是一个很好的例子来说明我遇到的问题。然而,在我的cshtml页面中使用以下内容调用searchForSimmers时,代码会编译:(使用tokenInput库)
<script type="text/javascript">
$(document).ready(function () {
$("#demo-input-local").tokenInput("/Admin/searchForSwimmers");
});
</script>
我收到500内部服务器错误。错误消息如下:
LINQ to Entities does not recognize the method 'Boolean ContainsAny(System.String, System.String[])' method, and this method cannot be translated into a store expression.
ContainsAny方法
public static Boolean ContainsAny(this String str, List<String> Fragments)
{
foreach (String fragment in Fragments)
{
if(str.Contains(fragment))
{
return true;
}
}
return false;
}
我环顾四周但找不到问题的解决方案。任何帮助都会非常感激,欢呼。
答案 0 :(得分:4)
查看我的博客,我在其中为IQueryable创建了一个搜索扩展方法。
更新:我创建了一个新的博客文章,其中显示了实现目标所需的扩展方法
http://www.ninjanye.co.uk/2013/04/generic-iqueryable-or-search-for.html
http://jnye.co/Posts/8/generic-iqueryable-or-search-for-multiple-search-terms-using-expression-trees
问题是linq不知道如何将代码翻译成SQL。
添加以下扩展方法:
public static class QueryableExtensions
{
public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, params string[] searchTerms)
{
if (!searchTerms.Any())
{
return source;
}
Expression orExpression = null;
foreach (var searchTerm in searchTerms)
{
//Create expression to represent x.[property].Contains(searchTerm)
var searchTermExpression = Expression.Constant(searchTerm);
var containsExpression = BuildContainsExpression(stringProperty, searchTermExpression);
orExpression = BuildOrExpression(orExpression, containsExpression);
}
var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, stringProperty.Parameters);
return source.Where(completeExpression);
}
private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd)
{
if (existingExpression == null)
{
return expressionToAdd;
}
//Build 'OR' expression for each property
return Expression.OrElse(existingExpression, expressionToAdd);
}
private static MethodCallExpression BuildContainsExpression<T>(Expression<Func<T, string>> stringProperty, ConstantExpression searchTermExpression)
{
return Expression.Call(stringProperty.Body, typeof(string).GetMethod("Contains"), searchTermExpression);
}
}
这将允许您编写以下lambda:
[HttpGet]
public JsonResult searchForSwimmers(string q)
{
//designed to return [{name='foo',id='bar'}]
String[] QueryTerms = q.Split(' '); //all the search terms are sep. by " "
var groupResults = _db.SwimGroups.Search(g => g.Name, queryTerms)
.OrderByDescending(g => g.Name.StartsWithAny(QueryTerms) ? 1 : 0)
.ThenBy( g => g)
.Select(g => new { name = g.Name, id = g.ID });
return Json(groupResults,JsonRequestBehavior.AllowGet);
}
答案 1 :(得分:2)
这是因为您的ContainsAny
或StartsWithAny
扩展方法都无法转换为SQL。
由于您的数据库表很小(如评论中所述),只需在执行.ToList()
和Where
之前调用OrderBy
即可解决您的查询。
试试这个:
var groupResults = _db.SwimGroups
.ToList() //evaluate the query, bring it into memory
.Where(g => g.Name.ContainsAny(QueryTerms))
.OrderByDescending(g => g.Name.StartsWithAny(QueryTerms) ? 1 : 0)
.ThenBy( g => g)
.Select(g => new { name = g.Name, id = g.ID });