【问题标题】:PredicateBuilder helper function for create expression用于创建表达式的 PredicateBuilder 辅助函数
【发布时间】:2019-06-26 02:50:16
【问题描述】:

我有一个表单来搜索条件,我使用 PredicateBuilder 将所有条件组合到一个 WHere 表达式 - 并且 EF 生成 sql 以在数据库端进行评估。

为了允许用户在相等、开头、结尾和包含之间进行选择,我使用星号通配符。

这是我的代码:

var predicate = LinqKit.PredicateBuilder.New<PersonEntity>(true);


{
    var arg = parameters.Name;
    arg = arg.Trim();

    var start = arg[0] == '*';
    var end = arg[arg.Length - 1] == '*';

    arg = arg.Trim('*');

    if (start && end)
        predicate.And(x => x.Name.Contains(arg));
    else if (start)
        predicate.And(x => x.Name.StartsWith(arg));
    else if (end)
        predicate.And(x => x.Name.EndsWith(arg));
    else
        predicate.And(x => x.Name == arg);
}

{
    var arg = parameters.Address;
    arg = arg.Trim();

    var start = arg[0] == '*';
    var end = arg[arg.Length - 1] == '*';

    arg = arg.Trim('*');

    if (start && end)
        predicate.And(x => x.Address.Contains(arg));
    else if (start)
        predicate.And(x => x.Address.StartsWith(arg));
    else if (end)
        predicate.And(x => x.Address.EndsWith(arg));
    else
        predicate.And(x => x.Address == arg);
}

结束等...

我想写一个通用的辅助函数,方便使用:

predicate.And(Helper.AddStringCompareCriteria(x => x.Name, parameters.Name);
predicate.And(Helper.AddStringCompareCriteria(x => x.Address, parameters.Address);

我现在的尝试:

public static class Helper
{
    static Type strType = typeof(string);
    static MethodInfo strStart = typeof(string).GetMethod("StartsWith");
    static MethodInfo strEnd = typeof(string).GetMethod("EndsWith");
    static MethodInfo strContains = typeof(string).GetMethod("Contains");
    static MethodInfo strEquals = typeof(string).GetMethod("Equals");

    static MethodInfo RightMethod(bool start, bool end)
    {
        if (start && end)
            return strContains;
        if (start)
            return strStart;
        if (end)
            return strEnd;
        else
            return strEquals;
    }

    public static Expression<Func<T, bool>> AddStringCompareCriteria<T, TResult>(Expression<Func<T, TResult>> member, string toComprae)
    {
        var arg = toComprae;
        arg = arg.Trim();

        var start = arg[0] == '*';
        var end = arg[arg.Length - 1] == '*';
        arg = arg.Trim('*');

        MethodInfo method = RightMethod(start, end);

        ParameterExpression entityParam = Expression.Parameter(typeof(T), "entity");


        return Expression.Lambda<Func<T, bool>>(
            Expression.Call(/* get-member-here?*/ , method, new[] { Expression.Constant(arg) }),
            entityParam);
    }
}

我现在不知道如何访问选定的成员(通过函数表达式), 而且我不确定我的方向是否正确,我很乐意提供帮助!

【问题讨论】:

    标签: c# entity-framework expression-trees linqkit


    【解决方案1】:

    首先,您需要删除通用参数TResult,因为您的代码要求它是一个字符串。只需将该类型的所有用法替换为 string,因为任何其他类型都不适用于此方法。

    接下来,与其尝试“手动”构建整个表达式,不如构造一个接受字符串并使用适当的比较函数计算布尔值的表达式,然后将该表达式与提供的表达式结合起来表达。一般来说,我想说的是,只要您可以可能避免手动构建表达式,您就应该避免它。您的代码会更短、更容易理解,并且重要的是由编译器静态验证

    因此,我们将首先调整您的 RightMethod 以返回 Expression

    static Expression<Func<string, bool>> ComparisonExpression(bool start, bool end, string toCompare)
    {
        if (start && end)
            return value => value.Contains(toCompare);
        if (start)
            return value => value.StartsWith(toCompare);
        if (end)
            return value => value.EndsWith(toCompare);
        else
            return value => value.Equals(toCompare);
    }
    

    接下来,我们需要一种将两个表达式组合在一起的方法。这里我们要手动构建,但是可以写成任意两个任意表达式,这样就不需要每次组合两个表达式时都重新编写代码。

    public static Expression<Func<TSource, TResult>> Compose<TSource, TIntermediate, TResult>(
        this Expression<Func<TSource, TIntermediate>> first,
        Expression<Func<TIntermediate, TResult>> second)
    {
        var param = Expression.Parameter(typeof(TSource));
        var intermediateValue = first.Body.ReplaceParameter(first.Parameters[0], param);
        var body = second.Body.ReplaceParameter(second.Parameters[0], intermediateValue);
        return Expression.Lambda<Func<TSource, TResult>>(body, param);
    }
    

    其中使用以下代码来实际替换参数:

    public static Expression ReplaceParameter(this Expression expression,
        ParameterExpression toReplace,
        Expression newExpression)
    {
        return new ParameterReplaceVisitor(toReplace, newExpression)
            .Visit(expression);
    }
    public class ParameterReplaceVisitor : ExpressionVisitor
    {
        private ParameterExpression from;
        private Expression to;
        public ParameterReplaceVisitor(ParameterExpression from, Expression to)
        {
            this.from = from;
            this.to = to;
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == from ? to : base.Visit(node);
        }
    }
    

    现在你的方法非常简单,因为它除了调用我们的两个方法之外几乎什么都不做:

    public static Expression<Func<T, bool>> AddStringCompareCriteria<T>(Expression<Func<T, string>> member, string toCompare)
    {
        toCompare = toCompare.Trim();
    
        var start = toCompare[0] == '*';
        var end = toCompare[toCompare.Length - 1] == '*';
        toCompare = toCompare.Trim('*');
    
        return member.Compose(ComparisonExpression(start, end, toCompare));
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-08
      • 1970-01-01
      • 2020-10-21
      • 2021-11-06
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 2019-02-10
      相关资源
      最近更新 更多