我正在尝试使用 System.Linq 中的表达式库动态生成表达式。当我像这样初始化表达式时:
Expression<Func<WorkOrderView, bool>> expression = x => true;
然后在where条件下执行
await context.WorkOrderView.Where(expression).ToListAsync();
效果很好。并返回我相关视图中的所有记录。但如果通过使用此类和方法生成:
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var body = Expression.AndAlso(left.Body, right.Body);
var parameters = left.Parameters;
return Expression.Lambda<Func<T, bool>>(body, parameters);
}
}
并像这样生成:
//END DATE
if (filterModel.EndDate is not null)
{
temp = x => x.CreateDateTime <= filterModel.EndDate;
expression = expression.AndAlso(temp);
}
//MATERIAL
if (filterModel.MaterialId != 0)
{
temp = x => x.MaterialId == filterModel.MaterialId;
expression = expression.AndAlso(temp);
}
//LINE
if (filterModel.LineIds is not null && filterModel.LineIds.Count > 0)
{
temp = x => filterModel.LineIds.Contains(x.LineId);
expression = expression.AndAlso(temp);
}
///ETC. ETC.
它抛出此错误:
InvalidOperationException:无法翻译 LINQ 表达式“x”。以可翻译的形式重写查询,或者通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用来显式切换到客户端计算。请参阅 https://go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。
AndAlso
类的ExpressionExtensions
方法尝试组合两个表达式的主体,但没有考虑到需要用左侧表达式的参数替换右侧表达式的参数。
您可能会遇到一些问题,因为表达式中的参数即使具有相同的名称,也不代表相同的引用。
解决这个问题的一种方法是使用 ExpressionVisitor 将右侧表达式的参数替换为左侧表达式的参数。
一个例子是:
public class ParameterUpdateVisitor : ExpressionVisitor
{
private readonly ParameterExpression _oldParameter;
private readonly ParameterExpression _newParameter;
public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
{
_oldParameter = oldParameter;
_newParameter = newParameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (object.ReferenceEquals(node, _oldParameter))
return _newParameter;
return base.VisitParameter(node);
}
}
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var visitor = new ParameterUpdateVisitor(right.Parameters[0], left.Parameters[0]);
var visitedRight = visitor.Visit(right.Body);
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left.Body, visitedRight), left.Parameters);
}
}
然后像这样使用它:
var filterModel = new FilterModel
{
EndDate = DateTime.Now,
MaterialId = 1,
LineIds = new List<int> { 1, 2, 3 }
};
Expression<Func<WorkOrderView, bool>> expression = x => true;
if (filterModel.EndDate is not null)
{
Expression<Func<WorkOrderView, bool>> temp = x => x.CreateDateTime <= filterModel.EndDate;
expression = expression.AndAlso(temp);
}
if (filterModel.MaterialId != 0)
{
Expression<Func<WorkOrderView, bool>> temp = x => x.MaterialId == filterModel.MaterialId;
expression = expression.AndAlso(temp);
}
if (filterModel.LineIds is not null && filterModel.LineIds.Count > 0)
{
Expression<Func<WorkOrderView, bool>> temp = x => filterModel.LineIds.Contains(x.LineId);
expression = expression.AndAlso(temp);
}
var result = await context.WorkOrderView.Where(expression).ToListAsync();
瞧!
您可以在 MS 文档此处查看 ExpressionVisitor 的示例。