[英]LINQ to Entities: Orderby statement involving extension method
我正在為網站構建一個簡單的搜索功能。 目的是允許用戶使用“鮑勃法杖”之類的查詢找到“游泳者”並將其通過搜索框添加到列表中。
我決定解決的第一部分是允許用戶通過組名進行搜索(在數據庫中,所有游泳者都是組的一部分)。 以下是我目前擁有的代碼。
[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;
}
我環顧四周,但找不到解決問題的方法。 任何幫助將不勝感激,歡呼。
在我的博客中創建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);
}
那是因為您的ContainsAny
或StartsWithAny
擴展方法都不能轉換為SQL。
由於數據庫表很小(如注釋中所述),因此在執行Where
和OrderBy
之前,只需調用.ToList()
即可解決查詢。
嘗試這個:
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 });
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.