C# 使用EFCore时,尝试在Where()子句中使用表达式树时出错
尝试通过组合其他表达式来编写C# 使用EFCore时,尝试在Where()子句中使用表达式树时出错,c#,entity-framework,linq,expression,C#,Entity Framework,Linq,Expression,尝试通过组合其他表达式来编写表达式时出错。目的是在EFCore查询的where()子句中使用它。例如: // Only get users that have been forwarded the request... var request = GetSomeRequestToFilterOn(); var filter = HasRequestBeenForwaredToUserExpression().ReplaceParameter(request); var results = _co
表达式时出错。目的是在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参数替换为提供的参数,将表达式
转换为和表达式
。这在我的单元测试中效果很好,但EFCore似乎不喜欢它,在实际数据库上运行时,我会遇到以下错误:
System.InvalidOperationException:将子表达式从类型System.Nullable
重写为类型System.Collections.Generic.IEnumerable
不允许,因为这将更改操作的含义。
如果这是有意的,请覆盖“Visiturary”并将其更改为允许
这是重写。在
System.Linq.Expressions.ExpressionVisitor.ValidateChildType(类型
before,Type after,String methodName)位于
System.Linq.Expressions.ExpressionVisitor.ValidateUnary(UnaryExpression
在…之前,在…之后)
System.Linq.Expressions.ExpressionVisitor.Visiturary(UnaryExpression
节点)在
System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor
访问者)在System.Linq.Expressions.ExpressionVisitor.visitor(表达式
节点)位于
基本上,如果在最后一个“等待批准”事件之后有任何“转发”事件,即当有新的“等待批准”事件时,先前的“转发”事件不再计算(但仍然存在),则认为某人已“转发”转移请求
这是我的过滤器
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);
}
内部静态表达式hasRequestBeenForwardToUserExpression()
{
//仅在最后一次“等待批准”事件计数后“转发”事件。
//因此,我们需要从请求中获取最后一个“等待批准”事件。
var projection=lastWaitingApprovalEvent();
var userParam=Expression.Parameter(typeof(ApplicationUser));
var requestParam=Expression.Parameter(typeof(TransferRequest));
var requestToEvent=projection.Body.ReplaceParameter(projection.Parameters[0],requestParam);
表达条件=
(用户,rqst,evt)=>evt!=null&&rqst.TransferRequestEvents
.任何(e2=>
等于(e2.EventType,EventType.Forwarded)&&
等于(e2.User,User)&&
e2.EventDateTime>evt.EventDateTime);
var body=条件。body
.ReplaceParameter(条件.Parameters[0],userParam)
.ReplaceParameter(条件参数[1],requestParam)
.ReplaceParameter(条件参数[2],requestToEvent);
返回表达式.Lambda(body、userParam、requestParam);
}
此方法获取最后一个“等待批准”事件
内部静态表达式lastWaitingApprovalEvent()
{
返回t=>t.TransferRequestEvents
.OrderBy(e=>e.EventDateTime).ThenBy(e=>e.Id)
.LastOrDefault(e=>Equals(e.EventType,EventType.WaitingReview));
}
我有一个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);
}
}
公共类ExpressionUtility
{
公共静态表达式替换参数(此表达式,
参数表达式替换,
表达式(新表达式)
{
返回新参数replaceVisitor(存储替换、新表达式)
.参观(表达);
}
公共静态表达式替换参数(此表达式源,TArg1 arg1)
{
var t1Param=表达式常数(arg1);
var t2Param=表达式参数(typeof(TArg2));
var body=source.body
.ReplaceParameter(源.Parameters[0],t1Param)
.ReplaceParameter(源.Parameters[1],t2Param);
返回表达式.Lambda(body,t2Param);
}
公共静态表达式替换参数(此表达式源,TArg2 arg2)
{
var t1Param=表达式参数(typeof(TArg1));
var t2Param=表达式常数(arg2);
var body=source.body
.ReplaceParameter(源.Parameters[0],t1Param)
.ReplaceParameter(源.Parameters[1],t2Param);
返回表达式.Lambda(body,t1Param);
}
}
公共类参数replaceVisitor:ExpressionVisitor
{
来自的私有参数表达式;
私人表达;
公共参数ReplaceVisitor(参数表达式从,表达式到)
{
this.from=from;
这个;
}
受保护的重写表达式VisitParameter(ParameterExpression节点)
{
返回节点==从?到:base.VisitParameter(节点);
}
}
编辑
当我将最终表达式的ToString打印到控制台时。我明白了
{Param_0=>
((值(debug.Models.TransferRequest.TransferRequestEvents.OrderBy)(e)
=>e.EventDateTime)。然后按(e=>e.Id)。LastOrDefault(e=>Equals(e.EventType,EventType.WaitingReview))!=null)和
值(debug.Models.TransferRequest).TransferRequestEvents.Any(e2
=>((等于(e2.EventType,EventType.Forwarded)和(e2.User,Param_0))和(e2.EventDateTime>
值(debug.Models.TransferRequest).TransferRequestEvents.OrderBy(e
=>e.EventDateTime)。然后按(e=>e.Id)。LastOrDefault(e=>Equals(e.EventType,EventType.WaitingReview)).EventDateTime))}
另外,下面的代码也可以正常工作
Expression<Func<string, ApplicationUser, bool>> filter = (s, u) => u.FirstName.Contains(s);
var whereClause= filter.ReplaceParameter("mark");
_context.AppicationUsers.Where(whereClause).ToList();
expressionfilter=(s,u)=>u.FirstName.Contains;
var whereClause=filter.ReplaceParameter(“标记”);
_context.applicationUsers.Where(Where子句).ToList();
Expression<Func<string, ApplicationUser, bool>> filter = (s, u) => u.FirstName.Contains(s);
var whereClause= filter.ReplaceParameter("mark");
_context.AppicationUsers.Where(whereClause).ToList();