Эта строка, например:
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
, которая не принимает предикат. Если вам нужно это использовать, вам нужно изменить код.