简体   繁体   English

C#将表达式作为参数传递并使用PredicateBuilder

[英]C# Pass expression as argument and use PredicateBuilder

I want to dynamically build LinqToSQL and EntityFramework query clauses using a "criteria" object and if necessary leveraging the C# In a Nutshell PredicateBuilder ( http://www.albahari.com/nutshell/predicatebuilder.aspx ) 我想使用“标准”对象动态构建LinqToSQL和EntityFramework查询子句,如果需要,在Nutshell PredicateBuilder中利用C#( http://www.albahari.com/nutshell/predicatebuilder.aspx

Just to be clear - I don't understand how to create C# expressions ;o) 只是要清楚 - 我不明白如何创建C#表达式; o)

The filter operator and value are defined as a property of my "query criteria" class as such: 过滤器运算符和值被定义为我的“查询条件”类的属性,如下所示:

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

The "filter" defined as: “过滤器”定义为:

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

    public DateTime FilterToken { get; set; }
}

And the "filter operator" is a plain C# enum: 而“过滤器运算符”是一个简单的C#枚举:

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

I want to dynamically build my LinqToSQL and EntityFramework queries in my repositories using an approach such as: 我想使用以下方法在我的存储库中动态构建我的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);
}

The above all works but I want my code to be DRY and when I add another property to my criteria (eg DateTimeStarted) I don't want my repository to duplicate the code immediately above for the new property. 以上都可以,但我希望我的代码是干的,当我添加另一个属性到我的标准(例如DateTimeStarted)时,我不希望我的存储库在上面为新属性复制代码。

So I want to simplify the above code to something like: 所以我想将上面的代码简化为:

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

Is this possible and if so how? 这有可能吗?如果可以的话怎么样?

It's definitely possible (and there is no need for PredicateBuilder). 这绝对是可能的(并且不需要PredicateBuilder)。 All you need is to map the supported FilterOperator to the corresponding Expression . 您所需要的只是将支持的FilterOperator映射到相应的Expression

Here is a custom extension method that does that: 这是一个自定义扩展方法,它执行以下操作:

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);
    }
}

and the sample usage: 和样本用法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM