c# - 使用 EFCore 时尝试在 Where() 子句中使用表达式树时出错
问题描述
我在尝试Expression<Func<ApplicationUser, bool>>
通过组合其他表达式来组合时遇到错误。目的是在 EFCore 查询的 where() 子句中使用它。例子:
// Only get users that have been forwarded the request...
var request = GetSomeRequestToFilterOn();
var filter = HasRequestBeenForwaredToUserExpression().ReplaceParameter(request);
var results = _context.ApplicationUsers.Where(filter).ToList();
在上面的示例中,我将表达式中的 TransferRequest 参数转换为提供的参数Expression<Func<ApplicationUser, TransferRequest, bool>>
。Expression<Func<ApplicationUser, bool>>
这在我的单元测试中效果很好,但 EFCore 似乎不喜欢它,并且在针对实际数据库运行时出现此错误:
System.InvalidOperationException:不允许将子表达式从一个类型重写
System.Nullable<System.DateTime>
为另一个类型,因为它会改变操作的含义。System.Collections.Generic.IEnumerable<System.Nullable<System.DateTime>>
如果这是故意的,请覆盖“VisitUnary”并将其更改为允许此重写。在 System.Linq.Expressions.ExpressionVisitor.ValidateChildType(Type before, Type after, String methodName) 在 System.Linq.Expressions.ExpressionVisitor.ValidateUnary(UnaryExpression before, UnaryExpression after) 在 System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression 节点) 在 System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor) 在 System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) 在...
基本上,如果在最后一个“等待批准”事件之后有任何“转发”事件,即当有一个新的“等待批准”事件时,之前的“转发”事件不存在,则认为一个人已被“转发”转移请求'不再计数(但仍然存在)。
这是我的过滤器。
internal static Expression<Func<ApplicationUser, TransferRequest, bool>> HasRequestBeenForwaredToUserExpression()
{
//only "Forwarded" events after the last "Awaiting Approval" event count.
//so we need to get the last "Awaiting Approval" event from the request.
var projection = LastAwaitingApprovalEvent();
var userParam = Expression.Parameter(typeof(ApplicationUser));
var requestParam = Expression.Parameter(typeof(TransferRequest));
var requestToEvent = projection.Body.ReplaceParameter(projection.Parameters[0], requestParam);
Expression<Func<ApplicationUser, TransferRequest, TransferRequestEvent, bool>> condition =
(user, rqst, evt) => evt != null && rqst.TransferRequestEvents
.Any(e2 =>
Equals(e2.EventType, EventType.Forwarded) &&
Equals(e2.User, user) &&
e2.EventDateTime > evt.EventDateTime);
var body = condition.Body
.ReplaceParameter(condition.Parameters[0], userParam)
.ReplaceParameter(condition.Parameters[1], requestParam)
.ReplaceParameter(condition.Parameters[2], requestToEvent);
return Expression.Lambda<Func<ApplicationUser, TransferRequest, bool>>(body, userParam, requestParam);
}
此方法获取最后一个“Awaiting Approval”事件
internal static Expression<Func<TransferRequest, TransferRequestEvent>> LastAwaitingApprovalEvent()
{
return t => t.TransferRequestEvents
.OrderBy(e => e.EventDateTime).ThenBy(e => e.Id)
.LastOrDefault(e => Equals(e.EventType, EventType.AwaitingReview));
}
我有一个 ExpressionUtility 类来完成替换参数的工作:
public class ExpressionUtility
{
public static Expression ReplaceParameter(this Expression expression,
ParameterExpression toReplace,
Expression newExpression)
{
return new ParameterReplaceVisitor(toReplace, newExpression)
.Visit(expression);
}
public static Expression<Func<TArg2, TReturn>> ReplaceParameter<TArg1, TArg2, TReturn>(this Expression<Func<TArg1, TArg2, TReturn>> source, TArg1 arg1)
{
var t1Param = Expression.Constant(arg1);
var t2Param = Expression.Parameter(typeof(TArg2));
var body = source.Body
.ReplaceParameter(source.Parameters[0], t1Param)
.ReplaceParameter(source.Parameters[1], t2Param);
return Expression.Lambda<Func<TArg2, TReturn>>(body, t2Param);
}
public static Expression<Func<TArg1, TReturn>> ReplaceParameter<TArg1, TArg2, TReturn>(this Expression<Func<TArg1, TArg2, TReturn>> source, TArg2 arg2)
{
var t1Param = Expression.Parameter(typeof(TArg1));
var t2Param = Expression.Constant(arg2);
var body = source.Body
.ReplaceParameter(source.Parameters[0], t1Param)
.ReplaceParameter(source.Parameters[1], t2Param);
return Expression.Lambda<Func<TArg1, TReturn>>(body, t1Param);
}
}
public class ParameterReplaceVisitor : ExpressionVisitor
{
private ParameterExpression from;
private Expression to;
public ParameterReplaceVisitor(ParameterExpression from, Expression to)
{
this.from = from;
this.to = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node == from ? to : base.VisitParameter(node);
}
}
编辑
当我将最终表达式的 ToString 打印到控制台时。我明白了
{Param_0 => ((值(Debugging.Models.TransferRequest).TransferRequestEvents.OrderBy(e => e.EventDateTime).ThenBy(e => e.Id).LastOrDefault(e => Equals(e.EventType, EventType. AwaitingReview)) != null) AndAlso value(Debugging.Models.TransferRequest).TransferRequestEvents.Any(e2 => ((Equals(e2.EventType, EventType.Forwarded) AndAlso Equals(e2.User, Param_0)) AndAlso (e2. EventDateTime > value(Debugging.Models.TransferRequest).TransferRequestEvents.OrderBy(e => e.EventDateTime).ThenBy(e => e.Id).LastOrDefault(e => Equals(e.EventType, EventType.AwaitingReview))。事件日期时间))))}
此外,下面的代码工作得很好
Expression<Func<string, ApplicationUser, bool>> filter = (s, u) => u.FirstName.Contains(s);
var whereClause= filter.ReplaceParameter("mark");
_context.AppicationUsers.Where(whereClause).ToList();
解决方案
推荐阅读
- postman - 邮递员在无法获得任何响应错误后如何继续运行
- javascript - 当 knex 使用 async/await 抛出错误时结束响应
- r - 如何组合 cuminc 曲线列表?例如使用ggcompetingrisks?
- python - 需要固定大小的类型通过套接字作为非常量数据的标头发送
- typescript - 带有对象和数组的树的 Typescript 递归映射类型
- php - 为什么数组合并在 For 循环中不起作用
- laravel - “无法声明类 App\Http\Controllers\category,因为该名称已在使用中”
- python - 为什么它不会将_keys 发送到评论框?
- reactjs - React-Redux 正在为每个 reducer 函数创建存储状态。为什么?以及如何解决?
- dependency-injection - Magento 2 命令有什么作用?