Как создать динамическое условие с помощью дерева выражений - PullRequest
1 голос
/ 13 марта 2012

Пожалуйста, рассмотрите этот код:

System.Linq.Expressions.Expression<Func<tbl, bool>> exp_details = r => r.ID_Master == Id &&
r.Year == Year &&
r.Month == Month ;

Я хочу написать функцию, которая ожидает некоторый аргумент, а затем извлечь некоторые данные из моей базы данных. Проблема в том, что я хочу создать динамический пример condition.dor, если я передаю IsDeleted аргумент со значением true, которое я хочу добавить r.IsDeleted == true; к exp_details. Как я могу это сделать?

Ответы [ 2 ]

2 голосов
/ 13 марта 2012

Ключом к этому является использование ExpressionVisitor, которое обходит заданное выражение и (необязательно, в подклассе) позволяет заменять распознанные элементы другими, указанными вами.В моем случае это было сделано для ORM (NHibernate).Вот что я использую: (я добавлю ссылки на мой ответ позже)

public class ParameterAssignAndReplacer : ExpressionVisitor
{
    private readonly ParameterExpression _source;
    private readonly ConstantExpression _target;

    internal ParameterAssignAndReplacer(ParameterExpression source, ConstantExpression target)
    {
        _source = source;
        _target = target;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node.Name == _source.Name ?
            base.VisitConstant(_target) :
            base.VisitParameter(node);
    }
}

И ..

public static class ExpressionExtensions
{
    public static Expression<Func<TArg2, TResult>> AssignAndReduce<TArg1, TArg2, TResult>(
        this Expression<Func<TArg1, TArg2, TResult>> func,
        TArg1 parameter)
    {
        var exprBody = func.Body;
        var assignedParameter = Expression.Constant(parameter, typeof(TArg1));
        exprBody = new ParameterAssignAndReplacer(func.Parameters[0], assignedParameter).Visit(exprBody);
        return Expression.Lambda<Func<TArg2, TResult>>(exprBody, func.Parameters[1]);
    }
}

Оба класса могут быть расширены для вашего конкретного сценария.Чтобы сделать это релевантным для кода, который вы разместили (я использую только IsDeleted в качестве параметра для простоты):

public class SomeClass
{
    Expression<Func<tbl, bool, bool>> _templateExpression =
       (tbl r, bool isDeleted) => r.ID_Master == 5 && r.Year == 2008 && r.Month == 12 && r.IsDeleted == isDeleted;

    public Expression<Func<tbl, bool>> Foo(bool IsDeleted)
    {
        return _templateExpression.AssignAndReduce(IsDeleted);
    }
}

Что касается ссылок, большая часть того, что я узнал по этой теме, взята из «Выражения» Марка Гравелла отвечают [хотя многие другие пользователи помогли мне разобраться в этом :-)]

1 голос
/ 13 марта 2012

вы не можете использовать тело оператора для выражения Linq рассмотрите возможность использования предиката вместо

        var exp_details = new Predicate<tbl>(r =>
        {
            bool result == Id && r.Year == Year && r.Month == Month;
            if(IsDeleted != null)
            {
                result &= r.IsDeleted == IsDeleted;
            }
            return result;
        });

наибольшее Linq Func выражение можно заменить на Predicate .

Для генерации динамического ГДЕ вызывается использование "простого" выражения linq.

IQueryable<tbl> query = ent.tbl.Where(r => r.ID_Master == Id && r.Year == Year);
//customize query
if(IsDeleted != null){
  query = query.Where(r => r.IsDeleted == IsDeleted);
}
//execute the final generated query
var result = query.FirstOrDefault();

это создаст причину от IQueryable<T>. Linq достаточно умен для сложного запроса.

...