使用EFCore时尝试在Where()子句中使用表达式树时出错

时间:2018-10-04 00:02:15

标签: c# entity-framework linq expression

在尝试通过组合其他表达式来组成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   之前,之后输入,字符串methodName)在   System.Linq.Expressions.ExpressionVisitor.ValidateUnary(UnaryExpression   之前,UnaryExpression之后)在   System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression   节点)   System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor   visitor)at System.Linq.Expressions.ExpressionVisitor.Visit(Expression   节点)...

基本上,如果在上次“等待批准”事件之后发生任何“转发”事件,即当有新的“等待批准”事件之前的“转发”事件发生,则认为该人已“转发”了转移请求'事件不再计数(但仍然存在)。

这是我的过滤器。

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);
}

此方法获取最后一个“等待批准”事件

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   值(Debugging.Models.TransferRequest).TransferRequestEvents.Any(e2   =>(((Equals(e2.EventType,EventType.Forwarded)AndAlso Equals(e2.User,Param_0))AndAlso(e2.EventDateTime>   值(Debugging.Models.TransferRequest).TransferRequestEvents.OrderBy(e   => e.EventDateTime).ThenBy(e => e.Id).LastOrDefault(e => Equals(e.EventType,EventType.AwaitingReview))。EventDateTime)))))))}

此外,以下代码也可以正常工作

Expression<Func<string, ApplicationUser, bool>> filter = (s, u) => u.FirstName.Contains(s);
var whereClause= filter.ReplaceParameter("mark");
_context.AppicationUsers.Where(whereClause).ToList();

0 个答案:

没有答案