我使用表达式树扩展了对IQueryable的自定义搜索,它工作正常。此自定义搜索中有两组搜索结果,它们使用Concat方法在最后一行合并在一起。我想通过显示firstRankResult first和secondRankResult秒来保留顺序。当前的代码几乎可以工作,直到它被发送到带有分页的asp:linqdatasource的asp:gridview,并且由于某些原因超出了我的理解,LinqToSql在开始搞乱订单时抛出OrderBy。然后,最终结果查询变为:
SELECT Row_Number() Over( Order By ...all fields...) ...
这会影响结果的顺序,第一组不再显示在列表的顶部。
长话短说,我想到为每个结果集添加一个SearchRank字段,最后强制它由SearchRank订购。因此,如果每个IQueryable生成此SQL:SELECT F1,F2 FROM T1
我可以使它成为SELECT 1 AS [SearchRank],F1,F2 FROM T1
并且类似于下一个IQueryable。然后我将它们与SearchRank的订单连在一起。有没有想过是否可以使用动态表达式树来完成?
public static IQueryable<T> RankedSearch<T>(this IQueryable<T> source, string[] fieldNames, string[] searchKeywords)
{
if (source == null)
throw new ArgumentNullException();
//Building Expression Tree
var string0Expression = Expression.Constant("0", typeof(string));
var string1Expression = Expression.Constant("1", typeof(string));
var alwaysTrueExpression = Expression.Equal(string0Expression, string0Expression);
var alwaysFalseExpression = Expression.Equal(string0Expression, string1Expression);
Expression firstRankExpression = alwaysTrueExpression;
Expression secondRankExpression = alwaysFalseExpression;
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
ParameterExpression pe = Expression.Parameter(typeof(T), "type");
foreach (var keyword in searchKeywords)
{
Expression cumContainsExpression = alwaysFalseExpression;
Expression valueExpression = Expression.Constant(keyword, typeof(string));
foreach (var fieldName in fieldNames)
{
Expression propertyExpression;
var fieldNameTree = fieldName.Split('.');
if (fieldNameTree.Length > 1)
{
var fieldParent = Expression.Property(pe, fieldNameTree[0]);
propertyExpression = Expression.Property(fieldParent, fieldNameTree[1]);
}
else
{
propertyExpression = Expression.Property(pe, fieldName);
}
Expression containsExpression = Expression.Call(propertyExpression, containsMethod, valueExpression);
cumContainsExpression = Expression.OrElse(cumContainsExpression, containsExpression);
}
firstRankExpression = Expression.AndAlso(firstRankExpression, cumContainsExpression);
secondRankExpression = Expression.OrElse(secondRankExpression, cumContainsExpression);
}
MethodCallExpression whereCallExpressionFirstRank = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { source.ElementType },
source.Expression,
Expression.Lambda<Func<T, bool>>(firstRankExpression, new ParameterExpression[] { pe }));
IQueryable<T> firstRankResult = source.Provider.CreateQuery<T>(whereCallExpressionFirstRank);
//This becomes SELECT F1,F2 FROM T1 WHERE (F1 LIKE '%keyword%' AND F2 LIKE '%keyword%')
//DESIRED OUTPUT SELECT 1 AS [SearchRank], F1,F2 FROM T1 WHERE (F1 LIKE '%keyword%' AND F2 LIKE '%keyword%')
MethodCallExpression whereCallExpressionSecondRank = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { source.ElementType },
source.Expression,
Expression.Lambda<Func<T, bool>>(secondRankExpression, new ParameterExpression[] { pe }));
IQueryable<T> secondRankResult = source.Provider.CreateQuery<T>(whereCallExpressionSecondRank);
//This becomes SELECT F1,F2 FROM T1 WHERE (F1 LIKE '%keyword%' OR F2 LIKE '%keyword%')
//DESIRED OUTPUT SELECT 2 AS [SearchRank], F1,F2 FROM T1 WHERE (F1 LIKE '%keyword%' OR F2 LIKE '%keyword%')
return firstRankResult.Concat(secondRankResult.Except(firstRankResult));
}
答案 0 :(得分:0)
我明白了。
var firstRankResult1 = firstRankResult.Select(r => new
{
baseType = r,
RankId = 1
});
var secondRankResult1 = secondRankResult.Except(firstRankResult).Select(r => new
{
baseType = r,
RankId = 2
});
var unionedResult = firstRankResult1.Concat(secondRankResult1).OrderBy(o => o.RankId).Select(o => o.baseType);
return unionedResult;