实体框架-C#将表达式作为参数并使用PredicateBuilder

本文关键字:参数 PredicateBuilder 框架 -C# 表达式 实体 | 更新日期: 2023-09-27 17:59:20

我想使用"criteria"对象动态构建LinqToSQL和EntityFramework查询子句,并在必要时利用C#In a Nutshell PredicateBuilder(http://www.albahari.com/nutshell/predicatebuilder.aspx)

我不明白如何创建C#表达式;o)

过滤运算符和值被定义为我的"查询条件"类的一个属性,如下所示:

public class QueryCriteria
{
    public DateTimeFilter DateTimeCompleted { get; set; }
}

"过滤器"定义为:

public class DateTimeFilter
{
    public FilterOperator FilterOperator { get; set; }
    public DateTime FilterToken { get; set; }
}

"过滤运算符"是一个普通的C#枚举:

public enum FilterOperator : int
{
    ExactMatch = 1,
    BeginsWith = 2,
    Contains = 3,
    EndsWith = 4,
    GreaterThan = 5,
    GreaterThanOrEqualTo = 6,
    LessThan = 7,
    LessThanOrEqualTo = 8,
}

我想使用以下方法在我的存储库中动态构建我的LinqToSQL和EntityFramework查询:

if (criteria.DateTimeCompleted != null)
{
    var predicate = PredicateBuilder.True(query);
    switch (criteria.DateTimeCompleted.FilterOperator)
    {
        case FilterOperator.ExactMatch:
            predicate = predicate.And(p => string.Equals(p.DateTimeCompleted.Value, criteria.DateTimeCompleted.FilterToken));
            break;
        case FilterOperator.GreaterThan:
            predicate = predicate.And(p => p.DateTimeCompleted.Value > criteria.DateTimeCompleted.FilterToken);
            break;
        case FilterOperator.GreaterThanOrEqualTo:
            predicate = predicate.And(p => p.DateTimeCompleted.Value >= criteria.DateTimeCompleted.FilterToken);
            break;
        case FilterOperator.LessThan:
            predicate = predicate.And(p => p.DateTimeCompleted.Value < criteria.DateTimeCompleted.FilterToken);
            break;
        case FilterOperator.LessThanOrEqualTo:
            predicate = predicate.And(p => p.DateTimeCompleted.Value <= criteria.DateTimeCompleted.FilterToken);
            break;
        default:
            throw new InvalidOperationException("Can't use the specified filter operator on a DateTime.");
    }
    query = query.Where(predicate);
}

以上都有效,但我希望我的代码是DRY,当我将另一个属性添加到我的条件(例如DateTimeStarted)时,我不希望我的存储库为新属性复制上面的代码。

所以我想将上面的代码简化为类似于:

query = query.Where(criteria.DateTimeCompleted.GetPredicate(query, p => p.DateTimeCompleted.Value));

这可能吗?如果可能,怎么做?

实体框架-C#将表达式作为参数并使用PredicateBuilder

这绝对是可能的(而且不需要PredicateBuilder)。您只需要将支持的FilterOperator映射到相应的Expression

这里有一个自定义的扩展方法:

public static class QueryableExtensions
{
    public static IQueryable<T> Where<T>(this IQueryable<T> source, DateTimeFilter filter, Expression<Func<T, DateTime>> target)
    {
        if (filter == null) return source;
        var left = target.Body;
        var right = Expression.Constant(filter.FilterToken);
        Expression condition;
        switch (filter.FilterOperator)
        {
            case FilterOperator.ExactMatch:
                condition = Expression.Equal(left, right);
                break;
            case FilterOperator.GreaterThan:
                condition = Expression.GreaterThan(left, right);
                break;
            case FilterOperator.GreaterThanOrEqualTo:
                condition = Expression.GreaterThanOrEqual(left, right);
                break;
            case FilterOperator.LessThan:
                condition = Expression.LessThan(left, right);
                break;
            case FilterOperator.LessThanOrEqualTo:
                condition = Expression.LessThanOrEqual(left, right);
                break;
            default:
                throw new InvalidOperationException("Can't use the specified filter operator on a DateTime.");
        }
        var predicate = Expression.Lambda<Func<T, bool>>(condition, target.Parameters);
        return source.Where(predicate);
    }
}

以及示例用法:

query = query.Where(criteria.DateTimeCompleted, p => p.DateTimeCompleted.Value);