PredicateBuilder帮助函数,用于创建表达式

时间:2019-02-01 13:56:38

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

我有一种搜索条件的表格,并且我使用PredicateBuilder将所有的criteras组合成一个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);
    }
}

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

1 个答案:

答案 0 :(得分:2)

因此,首先,您需要删除通用参数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));
}