Func <T, bool> для Any () IEnumerable - PullRequest
       34

Func <T, bool> для Any () IEnumerable

0 голосов
/ 29 октября 2018

Я пытаюсь запросить (linq к объектам 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 голосов
/ 31 октября 2018

Эта строка, например:

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

0 голосов
/ 29 октября 2018

Мне кажется, ваша проблема в вашем определении

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

...