Sam*_*our 6 c# expression entity-framework-core
我正在尝试在 .NET Core 应用程序中使用 C# 和 Entity Framework Core 3 构建“And”谓词方法。
该函数将两个表达式相加,并将其传递给 IQueryable 代码:
public Expression<Func<T, bool>> AndExpression<T>
(Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var andExpression = Expression.AndAlso(
left.Body, Expression.Invoke(right,
left.Parameters.Single()));
return Expression.Lambda<Func<T, bool>>(andExpression, left.Parameters);
}
Run Code Online (Sandbox Code Playgroud)
函数的调用
Expression<Func<Entity, bool>> left = t => t.Id == "id1";
Expression<Func<Entity, bool>> right = t => t.Id == "id2";
var exp = AndExpression(left, right);
this.dbContext.Set<Entity>().source.Where(exp).ToList();
Run Code Online (Sandbox Code Playgroud)
我的代码在 EF Core 2 版本中运行良好,但在我将版本更新到版本 3 后,它抛出以下异常
LINQ 表达式 'Where( source: DbSet, predicate: (s) => (t => t.Id == "id1") && Invoke(t => t.Id == "id2") )' 不能是翻译。以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。
由于内存问题,我无法将查询转换为 Enumerable。我明白这个问题,但我不知道是否有办法避免它。
如果有人给我提示,我将不胜感激。非常感谢!
您需要解开 lambdas 的主体,而不是使用Expression.Invoke. 您还必须至少重写一个 lambda 表达式,以便它们都使用相同的参数。
我们将使用 anExpressionVisitor用左侧的相应参数替换右侧的参数。然后我们将构造AndExpressionusing left 的 body 和从 right 重写的 body,最后创建一个新的 lambda。
public class ParameterReplaceVisitor : ExpressionVisitor
{
public ParameterExpression Target { get; set; }
public ParameterExpression Replacement { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Target ? Replacement : base.VisitParameter(node);
}
}
public static Expression<Func<T, bool>> AndExpression<T>(
Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var visitor = new ParameterReplaceVisitor()
{
Target = right.Parameters[0],
Replacement = left.Parameters[0],
};
var rewrittenRight = visitor.Visit(right.Body);
var andExpression = Expression.AndAlso(left.Body, rewrittenRight);
return Expression.Lambda<Func<T, bool>>(andExpression, left.Parameters);
}
Run Code Online (Sandbox Code Playgroud)
这会产生一个带有以下 DebugView 的 lambda:
public class ParameterReplaceVisitor : ExpressionVisitor
{
public ParameterExpression Target { get; set; }
public ParameterExpression Replacement { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Target ? Replacement : base.VisitParameter(node);
}
}
public static Expression<Func<T, bool>> AndExpression<T>(
Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var visitor = new ParameterReplaceVisitor()
{
Target = right.Parameters[0],
Replacement = left.Parameters[0],
};
var rewrittenRight = visitor.Visit(right.Body);
var andExpression = Expression.AndAlso(left.Body, rewrittenRight);
return Expression.Lambda<Func<T, bool>>(andExpression, left.Parameters);
}
Run Code Online (Sandbox Code Playgroud)
而您的代码会生成带有以下 DebugView 的 lambda:
.Lambda #Lambda1<System.Func`2[System.String,System.Boolean]>(Entity $t) {
$t.Id == "id1" && $t.Id == "id2"
}
Run Code Online (Sandbox Code Playgroud)
看看你的如何从 lambda 中调用 lambda(EF 无法处理的东西),而我的只有一个 lambda。