C# 类型数组上的IQueryable筛选器

C# 类型数组上的IQueryable筛选器,c#,linq,entity-framework,ef-code-first,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&l

假设一个
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);
}
生成的表达式类似于

e => e is Circle || e is Rectange
这正是我想要的,但我得到了这个错误

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

甚至我在扩展方法的最后一行使用了的
AsExpandable()
方法:

return source.AsExpandable().Where(lambdaExpression);
但同样的错误

我也试过这样做,但还是同样的错误

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);
}
var finalExpression=PredicateBuilder.False();
foreach(类型中的变量类型)
{
var expressionParameter=Expression.Parameter(typeof(TSource),“it”);
var lambdaExpression=Expression.Lambda(
Expression.TypeIs(表达式,类型),
表达式参数);
最终表达式。或(lambdaExpression);
}
有什么问题

如何将EF中的
表达式
转换为
表达式

或者如何将
表达式
实例传递给
Where
扩展方法


或者如何使用EF的
Expression.TypeIs

您需要将参数传递给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);
    }

您需要将参数传递给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);
    }

如果(!types.Any())
可能会导致
NullReferenceException
@Steve-这不是我的问题。我知道,只是指出了它。我可以帮助您,但注意到您的示例可以在没有Linq.Expression命名空间的情况下完成。哦…我现在明白了…但不确定EF是否能工作。然而,这很简单。实际上,您需要使用Expression finalExpression=Expression.TypeIs(Expression.Body,types.First())。注意
.Body
。我花了几个小时的时间用LinqPad来解决这个问题,以找出如何实现动态Linq。您可能会发现还需要交换
ParameterExpression
的每个实例。另外,在数据库之前对数组进行尝试。LinqToObjects编译器比LinqToEF编译器更强大,有效的Linq可能无法在EF上编译。
如果(!types.Any())
可能会导致
NullReferenceException
@Steve-这不是我的问题。我知道,只是指出它。我可以帮助您,但请注意,您的示例可以在没有Linq.Expression名称空间的情况下完成。哦…我现在明白了…但不确定EF是否可以工作。然而,这很简单。实际上,您需要使用Expression finalExpression=Expression.TypeIs(Expression.Body,types.First())。注意
.Body
。我花了几个小时的时间用LinqPad来解决这个问题,以找出如何实现动态Linq。您可能会发现还需要交换
ParameterExpression
的每个实例。另外,在数据库之前对数组进行尝试。LinqToObjects编译器比LinqToEF编译器更强大,有效的Linq可能无法在EF上编译。它工作正常,但如果我想使用表达式指定应用过滤器的属性,该怎么办?例如
db.Shapes.OfTypes(s=>s.SomeProperty,types)。我的意思是这不可能吗?它工作正常,但是如果我想使用表达式来指定应用过滤器的属性呢?例如
db.Shapes.OfTypes(s=>s.SomeProperty,types)。我是说这不可能吗?
var types = new Type[] { typeof(Circle), typeof(Rectangle) };
var test = context.Shapes.OfTypes(types);