IQueryable过滤类型数组

时间:2013-07-18 12:11:08

标签: c# linq entity-framework ef-code-first

假设一个Type数组作为条件对象,指示必须显示的子类型。

型号:

public abstract class Shape { }

public class Circle : Shape { }

public class Rectangle : Shape { }

我已经实现了一个应用此类查询的扩展方法

public static IQueryable<TSource> OfTypes<TSource, TResult>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, TResult>> expression, 
    params Type[] types)
{
    if (!types.Any())
    {
        return source;
    }

    Expression finalExpression = Expression.TypeIs(expression, types.First());

    foreach (var type in types.Skip(1))
    {
        finalExpression = Expression.OrElse(
            Expression.TypeIs(expression, type),
            finalExpression);
    }

    var lambdaExpression = Expression.Lambda<Func<TSource, bool>>(
        finalExpression, 
        expression.Parameters);

    return source.Where(lambdaExpression);
}

然后我可以像这样使用它

var types = new Type[] { typeof(Circle), typeof(Rectange) };
db.Shapres.OfTypes(s => s, types);

生成的表达式类似于

e => e is Circle || e is Rectange

正是我想要的但是我得到了这个错误

  

LINQ to Entities中不支持LINQ表达式节点类型“Lambda”。

即使我在扩展方法的最后一行使用了LinqKitAsExpandable()方法:

return source.AsExpandable().Where(lambdaExpression);

但同样的错误。

我也尝试了PredicateBuilder这样但又犯了同样的错误

var finalExpression = PredicateBuilder.False<TSource>();

foreach (var type in types)
{
    var expressionParameter = Expression.Parameter(typeof(TSource), "it");
    var lambdaExpression = Expression.Lambda<Func<TSource, bool>>(
        Expression.TypeIs(expression, type), 
        expressionParameter);
    finalExpression.Or(lambdaExpression);
}

有什么问题?

如何在没有问题的情况下将EF中的Expression转换为Expression<Func<T, TResult>>

或者我如何将Expression实例传递给Where扩展方法?

或者如何将Expression.TypeIs用于EF?

1 个答案:

答案 0 :(得分:1)

您需要将参数传递给Lambda函数,然后传递传入的表达式,即lambda。尝试通过删除'expression'参数来创建扩展方法内部的参数,因为它不需要。

public static IQueryable<TSource> OfTypes<TSource>(
        this IQueryable<TSource> source,
        params Type[] types)
    {
        if (!types.Any())
        {
            return source;
        }

        var param = Expression.Parameter(typeof(TSource), "p");

        Expression finalExpression = Expression.TypeIs(param, types.First());

        foreach (var type in types.Skip(1))
        {
            finalExpression = Expression.OrElse(
                Expression.TypeIs(param, type),
                finalExpression);
        }

        var lambdaExpression = Expression.Lambda<Func<TSource, bool>>(
            finalExpression, param);


        return source.Where(lambdaExpression);
    }

这样称呼:

var types = new Type[] { typeof(Circle), typeof(Rectangle) };
var test = context.Shapes.OfTypes(types);