将linq表达式(curry参数)传递给linq到实体

时间:2016-02-18 14:48:58

标签: c# entity-framework linq

所以我能够完成第一步,只允许有人使用表达式来获取我可以与字符串进行比较的属性。

现在我试图把它提升到一个新的水平,我希望人们能够做到这一点,其中"条款和我将替换值来比较。基本用例是我们的API接受名为?filterBy:AddressState:VA的查询参数。

我试图创建一些能够通常将句子附加到IQueryable的内容。基本上每个API都需要创建一个密钥/ Expressions字典,用于查找属性以比较filterBy的右侧,例如VA ..这是我目前所拥有的,但目前正在崩溃错误

我试图允许某人定义:

public Dictionary<string, Expression<Func<User, string, bool>>> FILTER_BY = new Dictionary<string, Expression<Func<User, string, bool>>>()  
{
    { "addressstate", (x,inputValue) => x.Address.State == inputValue},   
};

然后可以在api电话中做:

baseQ = baseQ.FilterBy(filterBy, FILTER_BY);

但这就是我被困的地方。我试图找出如何用字符串替换inputValue(在本例中)。

在一天结束时,我希望能够定义:

(x, inputValue) => x.Address.State == inputValue

我想写一些东西来替换上面的新表达式,将其传递给linq到实体

(x) => x.Address.State == "Va"

扩展方法:

public static IQueryable<T> FilterBy<T, CompareMe>(this IQueryable<T> query, string filterBy, Dictionary<string, Expression<Func<T, CompareMe, bool>>> filterExpressions)
    {
        if (!string.IsNullOrEmpty(filterBy))
        {
            //parse on ':' throw argument if there is not two.
            var split = filterBy.Split(':');
            var key = split[0];
            var right = string.Join("", split.Skip(1));


            var expression  =filterExpressions.FirstOrDefault(x=>x.Key == key.ToLower());
             if (expression.Key != null)
            {
                var parameter = Expression.Parameter(typeof(T), expression.Value.Parameters[0].Name);

                Expression body = new ReplaceVisitor<string>(expression.Value.Parameters[1], right).Visit(expression.Value.Body);
                var lambda =  Expression.Lambda<Func<T, bool>>(body, parameter);

                return query.Where(lambda);

            }
       }

    }

这是ReplaceVisitor:

  class ReplaceVisitor <CompareMe> : ExpressionVisitor
{
    private CompareMe _value;


     private ParameterExpression _parameter;

     public ReplaceVisitor(ParameterExpression parameter, CompareMe value)
    {
        _parameter = parameter;
        _value = value;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node.Name == _parameter.Name)
        {
            return Expression.Constant(_value);
        }
        return node;
    }
}

我使用linq实体,所以无论我放在哪个where子句需要安全转换为sql。

错误我得到了:

  

ExceptionMessage:&#34;参数&#39; x&#39;未绑定在指定的LINQ to Entities查询表达式中。&#34;,

2 个答案:

答案 0 :(得分:1)

所以最终我解决了它。我基本上需要使用Visitor将参数替换为常量,然后将original参数传递给Expression.Lambda

解决方案是改变:

var parameter = Expression.Parameter(typeof(T), expression.Value.Parameters[0].Name);

Expression body = new ReplaceVisitor<string>(expression.Value.Parameters[1], right).Visit(expression.Value.Body);                   

var lambda =  Expression.Lambda<Func<T, bool>>(body, parameter);

要:

Expression body = new ReplaceVisitor<string>(expression.Value.Parameters[1], right).Visit(expression.Value.Body);
 var lambda =  Expression.Lambda<Func<T, bool>>(body, expression.Value.Parameters[0]);

答案 1 :(得分:1)

您可以使用像这样的简单参数替换器

static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
    return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}

class ParameterReplacer : ExpressionVisitor
{
    public ParameterExpression Source;
    public Expression Target;
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node == Source ? Target : base.VisitParameter(node);
    }
}
你方法中的

public static IQueryable<T> FilterBy<T, CompareMe>(this IQueryable<T> query, string filterBy, Dictionary<string, Expression<Func<T, CompareMe, bool>>> filterExpressions)
{
    if (!string.IsNullOrEmpty(filterBy))
    {
        //parse on ':' throw argument if there is not two.
        var split = filterBy.Split(':');
        var key = split[0];
        var value = string.Join("", split.Skip(1));
        var expression = filterExpressions.FirstOrDefault(x => x.Key == key.ToLower()).Value;
        if (expression != null)
        {
            bool stringValue = typeof(CompareMe) != typeof(string);
            var valueExpr = value == null || (!stringValue && value == string.Empty) ?
                Expression.Constant(null, typeof(CompareMe)) :
                Expression.Constant(stringValue ?  value : Convert.ChangeType(value, typeof(CompareMe)));
            var body = expression.Body.ReplaceParameter(expression.Parameters[1], valueExpr);
            var lambda = Expression.Lambda<Func<T, bool>>(body, expression.Parameters[0]);
            return query.Where(lambda);
        }
    }
    return query;
}