所以我能够完成第一步,只允许有人使用表达式来获取我可以与字符串进行比较的属性。
现在我试图把它提升到一个新的水平,我希望人们能够做到这一点,其中"条款和我将替换值来比较。基本用例是我们的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;,
答案 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;
}