Как использовать одно выражение C # внутри другого выражения C # для Entity Framework? - PullRequest
1 голос
/ 04 апреля 2019

Предположим, у меня есть код C #, который выглядит следующим образом:

var query1 = query.Where(x => x.BirthDate > now);
var query2 = query.Where(x => x.EnrollmentDate > now);
var query3 = query.Where(x => x.GraduationDate > now);

Фактический код более сложный, но я использую простой пример. Я передаю этот код в Entity Framework.

Предположим, я смотрю на это и говорю: "Это НЕ СУХОЙ", а затем я пишу такую ​​функцию.

public IQueryable<Student> FilterAfterDate(IQueryable<Student> query,
    Expression<Func<Student, DateTime>> GetDateExpression, DateTime now)
{
    return query.Where(x => GetDateExpression(x) > now);
}

Во время выполнения выдает следующую ошибку:

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

Я не знаю подробностей, но я полагаю, что решение этого, вероятно, состоит в том, чтобы каким-то образом взять мой FilterAfterDate, равный Expression<Func<Student, DateTime>>, и каким-то образом объединить это с сравнением даты и времени для получения выражения типа Expression<Func<Student, bool>> перейти к функции Where, но я не знаю, как это сделать.

1 Ответ

0 голосов
/ 04 апреля 2019

Используя LINQKit , вы можете написать свой метод (я предпочитаю в качестве метода расширения) примерно так:

public static class StudentExt {
    public static IQueryable<Student> FilterAfterDate(this IQueryable<Student> query,
        Expression<Func<Student, DateTime>> GetDateExpression, DateTime now)
        => query.AsExpandable().Where(x => GetDateExpression.Invoke(x) > now);
}

И используйте это так:

var q1 = query.FilterAfterDate(q => q.BirthDate, now);
var q2 = query.FilterAfterDate(q => q.EnrollmentDate, now);
var q3 = query.FilterAfterDate(q => q.GraduationDate, now);

Чтобы бросить свой собственный, вы просто используете обычный ExpressionVisitor, который заменяет:

public static class ExpressionExt {
    /// <summary>
    /// Replaces an Expression (reference Equals) with another Expression
    /// </summary>
    /// <param name="orig">The original Expression.</param>
    /// <param name="from">The from Expression.</param>
    /// <param name="to">The to Expression.</param>
    /// <returns>Expression with all occurrences of from replaced with to</returns>
    public static Expression Replace(this Expression orig, Expression from, Expression to) => new ReplaceVisitor(from, to).Visit(orig);
}

/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
    readonly Expression from;
    readonly Expression to;

    public ReplaceVisitor(Expression from, Expression to) {
        this.from = from;
        this.to = to;
    }

    public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}

Теперь, когда доступно Replace, вы можете создать лямбда-шаблон и использовать его для замены в параметре Expression:

public static class StudentExt {
    public static IQueryable<Student> FilterAfterDate(this IQueryable<Student> query,
        Expression<Func<Student, DateTime>> GetDateExpression, DateTime now) {
        Expression<Func<DateTime,bool>> templateFn = x => x > now;
        var filterFn = Expression.Lambda<Func<Student,bool>>(templateFn.Body.Replace(templateFn.Parameters[0], GetDateExpression.Body), GetDateExpression.Parameters);

        return query.Where(filterFn);
    }
}

И вы используете его так же, как с LINQKit.

...