1

我正在为网站构建一个简单的搜索功能。目的是允许用户使用“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;
        }

我环顾四周,但找不到解决问题的方法。任何帮助将不胜感激,干杯。

4

2 回答 2

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);
}    
于 2013-04-28T20:43:07.170 回答
2

那是因为您的方法ContainsAnyStartsWithAny扩展方法都不能转换为 SQL。

由于您的数据库表很小(如注释中所述),只需在执行and.ToList()之前调用即可解决您的查询。WhereOrderBy

尝试这个:

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 });
于 2013-04-25T12:45:15.067 回答