首先,我想让你知道我在制作这个问题之前已经搜索了另一个问题和答案,但我找不到任何能帮助我解决这个问题的问题。
我需要根据其类的两个属性过滤寄存器,其中一个是与搜索对应的字段,另一个是必须在数据库中引用寄存器的另一个实体的数字代码。 / p>
我的搜索功能具有以下签名:
public List<TView> SearchByField(int parentCode, string fieldName, string filter);
我尝试使用Expression Trees实现这一点,并让它获得两个表达式,但现在我没有将这些表达式组合起来构建一个传递给最终的
Expression.AndAlso(parentCodeFilterExpression, textFilterExpression);
只将一个表达式组合在一起。
到目前为止,我得到的代码是下面显示的代码(对于长片段感到抱歉,但我认为这对于更容易理解这个问题是必要的):
public List<TView> SearchPerField(int parentCode, string fieldName, string filter)
{
var lambdaExpression = GetLambdaExpressionForSearchByField(fieldName, filter, parentCode);
return new PersistenciaImpl<TView>().Where(lambdaExpression).ToList();
}
private Expression<Func<TView, bool>> GetLambdaExpressionForSearchByField(string fieldName, string filter, int parentCode)
{
Expression<Func<TView, bool>> textFilterExpression = GetTextFilterExpression(fieldName, filter);
Expression<Func<TView, bool>> parentCodeFilterExpression = GetParentCodeFilterExpression(parentCode);
Expression.Lambda<Func<TView, bool>>(textFilterExpression, parentCodeFilterExpression);
// THIS IS THE POINT. HOW TO MAKE THIS WORK?
Expression.AndAlso(parentCodeFilterExpression, textFilterExpression);
return textFilterExpression;
}
private Expression<Func<TView, bool>> GetParentCodeFilterExpression(int parentCode)
{
ParameterExpression parameter = Expression.Parameter(typeof(TView), "x");
Expression parent = Expression.Property(parameter, "Parent");
Expression parentCodeExpression = Expression.Property(parent, "Code");
Expression target = Expression.Constant(parentCode);
Expression containsMethod = Expression.Call(parentCodeExpression, "Equals", null, target);
Expression<Func<TView, bool>> textFilterExpression =
Expression.Lambda<Func<TView, bool>>(containsMethod, parameter);
return textFilterExpression;
}
private Expression<Func<TView, bool>> GetTextFilterExpression(string fieldName, string filter)
{
ParameterExpression parameter = Expression.Parameter(typeof(TView), "x");
Expression property = Expression.Property(parameter, fieldName);
Expression target = Expression.Constant(filter.ToUpper());
Expression containsMethod = Expression.Call(property, "Contains", null, target);
Expression<Func<TView, bool>> textFilterExpression =
Expression.Lambda<Func<TView, bool>>(containsMethod, parameter);
return textFilterExpression;
}
感谢您的任何建议。
答案 0 :(得分:0)
您可以使用表达式上的Compile method将Expression<TDelegate>
编译为TDelegate
:
Expression<Func<TView, bool>> lambdaExpression =
GetLambdaExpressionForSearchByField(fieldName, filter, parentCode);
Func<TView, bool> func = lambdaExpression.Compile();
完成后,您可以将其用作Where
函数的参数。
使用上面的代码,您可以使用
return new PersistenciaImpl<TView>().Where(func).ToList();
答案 1 :(得分:0)
我认为你需要这样的东西:
MethodCallExpression where = Expression.Call((
typeof(Queryable),
"Where",
new Type[] { TView },
lambdaExpression );
请注意,我不认为这是一个解决方案;它只是一个想法或例子。也许this link会帮助你。
答案 2 :(得分:0)
我尝试使用Expression Trees实现这一点,并让它获得两个表达式,但现在我没有将这些表达式组合起来构建一个传递给最终的
首先,您需要为最终(外部)lambda声明一个参数。然后你需要独立地调用你的两个过滤器(内部)lambda,并将相同的参数传递给每个:
// using E = System.Linq.Expressions.Expression;
var item = E.Parameter(typeof(TView));
var combined = E.Lambda<Func<TView, bool>>(
E.AndAlso(
E.Invoke(parentCodeFilterExpression, item),
E.Invoke(textFilterExpression, item)),
item);
如果您需要这些表达式与Entity Framework等查询提供程序兼容,那么事情会变得有点混乱,因为可能不支持Invoke
表达式。你必须手动内联两个过滤器lambda,它需要遍历每个过滤器的主体,并用对外部lambda参数的引用替换内部参数引用:
// using E = System.Linq.Expressions.Expression;
sealed class ParameterReplacementVisitor : ExpressionVisitor
{
private readonly IDictionary<E, E> _replacements;
public ParameterReplacementVisitor(IDictionary<E, E> replacements)
{
_replacements = replacements;
}
protected override Expression VisitParameter(ParameterExpression node)
{
E replacement;
if (_replacements.TryGetValue(node, out replacement))
return this.Visit(replacement);
return base.VisitParameter(node);
}
}
// ...
var item = E.Parameter(typeof(TView));
var visitor = new ParameterReplacementVisitor(
new Dictionary<E, E> {
{ parentCodeFilterExpression.Parameters[0], item },
{ textFilterExpression.Parameters[0], item }
}
);
var combined = E.Lambda<Func<TView, bool>>(
E.AndAlso(
visitor.Visit(parentCodeFilterExpression.Body),
visitor.Visit(textFilterExpression.Body)),
item);
或者,如果你在帖子建议的情况下在封闭环境中编写内部表达式,你可以简单地将外部lambda参数作为参数传递给构造内部表达式的方法,并仅返回实体(不要打扰在lambdas中包裹内部过滤器。)