Вспомогательная функция PredicateBuilder для создания выражения - PullRequest
0 голосов
/ 01 февраля 2019

У меня есть форма для поиска критериев, и я использую 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);
}

end ect ...

я хочу написать универсальную вспомогательную функцию,для простого использования:

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 голосов
/ 01 февраля 2019

Итак, во-первых, вам нужно удалить универсальный аргумент 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));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...