InvalidOperationException:无法翻译 LINQ 表达式“x”

问题描述 投票:0回答:1

我正在尝试使用 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 了解更多信息。

c# linq
1个回答
0
投票

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 的示例。

© www.soinside.com 2019 - 2024. All rights reserved.