【问题标题】:IQueryable filter on array of types类型数组的 IQueryable 过滤器
【发布时间】:2013-07-18 12:11:08
【问题描述】:

假设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&lt;Func&lt;T, TResult&gt;&gt;

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

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

【问题讨论】:

  • if (!types.Any()) 可能会导致NullReferenceException
  • @Steve - 这不是我的问题。
  • 我知道,只是指出来。
  • 我可以帮忙,但请注意,您的示例可以在没有 Linq.Expression 命名空间的情况下完成。
  • 哦...我现在明白了...但不确定 EF 是否可以工作。但是,它非常简单。您实际上想使用Expression finalExpression = Expression.TypeIs(expression.Body, types.First());。注意.Body。我花了几个小时来解决这个问题,使用 LinqPad 弄清楚如何做动态 Linq。您可能会发现还需要换掉ParameterExpression 的每个实例。另外,在数据库之前对数组进行尝试。 LinqToObjects 编译器比 LinqToEF 编译器更强大,有效的 Linq 可能无法在 EF 上编译。

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


【解决方案1】:

您需要将参数传递给 Lambda 函数,您传递的是传入的表达式,它是一个 lambda。尝试通过删除不需要的“表达式”参数来在扩展方法内部创建参数。

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);

【讨论】:

  • 它可以正常工作,但是如果我想使用表达式来指定应用过滤器的属性怎么办?例如db.Shapes.OfTypes(s =&gt; s.SomeProperty, types);。我的意思是这不可能吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-17
  • 1970-01-01
  • 1970-01-01
  • 2020-07-29
  • 1970-01-01
相关资源
最近更新 更多