扩展IQueryable <t>以添加属性</t>

时间:2014-05-09 01:14:00

标签: c# asp.net sql linq expression-trees

我使用表达式树扩展了对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));
}

1 个答案:

答案 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;