在Any()IEnumerable

时间:2018-10-29 15:04:38

标签: c# linq-to-entities entity-framework-core predicate npgsql

我尝试查询(对实体EF Core的查询)导航属性集合,因此我使用any()像这样:

var query = context.MyTable.Where(x => x.mycollectionproperties.Any(p => p.myprop == myvar );

它工作得很好,但是现在我想构造谓词,而不是直接在查询中定义它。 所以我做:

Func<T, bool> mypredicate = (p => p.myprop == myvar);
var query = context.MyTable.Where(x => x.mycollectionproperties.Any(mypredicate);

(我用实体名称替换了T)

但这会产生错误:无法将类型为“ System.Linq.Expressions.TypedParameterExpression”的对象转换为类型为“ System.Linq.Expressions.LambdaExpression”。

我如何构造我的谓词以在Any()集合上使用它? 谢谢

2 个答案:

答案 0 :(得分:0)

例如以下行:

var query = context.MyTable.Where(x => x.mycollectionproperties.Any(p => p.myprop == 1));

在编译时将被编译为如下内容:

var xParameter = Expression.Parameter(typeof(Entity1), "x");

var pParameter = Expression.Parameter(typeof(Entity2), "p");

var anyMethod =
    typeof(Enumerable)
        .GetMethods()
        .Single(x => x.Name == "Any" && x.GetParameters().Length == 2)
        .MakeGenericMethod(typeof(Entity2));

var anyCondition = Expression.Lambda<Func<Entity2, bool>>(
    Expression.Equal(
        Expression.Property(
            pParameter,
            typeof(Entity2).GetProperty("myprop").GetMethod),
        Expression.Constant(1, typeof(int))),
    pParameter);

var query = context.MyTable.Where(
    Expression.Lambda<Func<Entity1, bool>>(
        Expression.Call(
            null,
            anyMethod,
            new Expression[] {
                Expression.Property(
                    xParameter,
                    typeof(Entity1).GetProperty("mycollectionproperties").GetMethod),
                anyCondition
            }),
        xParameter));

这称为表达式树。有关更多详细信息,请参见此参考资料: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/

尽管Any方法使用了Func,但是在构造表达式树时,请注意,Expression<Func<Entity2, bool>>方法中使用了表达式(Any)。

C#似乎没有办法给Any方法一个表达式而不是Func,即使整个事情都是一个表达式树(我的意思是像您想要实现的目标。

实现所需目标的最明显方法是使用本文中的代码,并将anyCondition变量替换为要用于Any内部条件的任何表达式。

另一种方法是“正常”地构造表达式树的一部分,并将null传递给Any方法,然后使用表达式访问者用表达式替换null。这种访客的样子如下:

public class AnyMethodArgumentReplacingVisitor : ExpressionVisitor
{
    private readonly Expression expression;

    public AnyMethodArgumentReplacingVisitor(Expression expression)
    {
        this.expression = expression;
    }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.Name == "Any")
        {
            return Expression.Call(node.Object, node.Method, node.Arguments[0], expression);
        }

        return base.VisitMethodCall(node);
    }
}

这是您将如何使用它:

Expression<Func<Entity2, bool>> predicate =
    a => a.myprop == 2;

Expression<Func<Entity1, bool>> expression =
    b => b.mycollectionproperties.Any(null);

var expression2 =
    (Expression<Func<Entity1, bool>>)
        new AnyMethodArgumentReplacingVisitor(predicate).Visit(expression);

请注意,此类访问者会将调用替换为任何Any方法。它还假定仅使用带有谓词的Any的重载。 Any的另一个重载不带谓词。如果需要使用它,则需要调整代码。

答案 1 :(得分:-1)

在我看来,您的问题出在您对

的定义中
Func<T, bool> mypredicate = (p => p.myprop == myvar);

您不应使用 T ,而应使用 mycollectionproperties

假设属性 mycollectionproperties 被定义为类似的内容

....
public IQueryable<YourType> mycollectionproperties { get; set; }
....

然后,您应将 mypredicate 声明为

Func<YourType, bool> mypredicate = (p => p.myprop == myvar);

您可以在.NetFiddle

上看到一个有效的示例。