首页 > 代码库 > .NET Core中合并Expression<Func<T,bool>>的正确姿势

.NET Core中合并Expression<Func<T,bool>>的正确姿势

这是在昨天的 .NET Core 迁移中遇到的问题,之前在 .NET Framework 中是这样合并 Expression<Func<T,bool>> 的:

public static class ExpressionBuilder{    public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)    {        var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);        var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);        return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);    }    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)    {        return first.Compose(second, Expression.And);    }        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)    {        return first.Compose(second, Expression.Or);    }}

迁移至 .NET Core 之后,Entity Framework Core 生成了不正确的 SQL 语句:

WHERE (CASE        WHEN ([q0].[IsActive] = 1) AND ([q.questionUser0].[IsActive] = 1)        THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)    END & CASE        WHEN [q0].[DealFlag] = 0        THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)    END) AND (([q0].[Flags] & @__flags_0) = @__flags_1)

在 Server Server 中执行时会出现如下的错误:

在应使用条件的上下文(在 ‘AND‘ 附近)中指定了非布尔类型的表达式

后来参考 Combining two expressions (Expression<Func<T, bool>>),通过下面的代码解决了问题:

public static class ExpressionBuilder{    public static Expression<Func<T, bool>> And<T>(        this Expression<Func<T, bool>> first,         Expression<Func<T, bool>> second)    {        return first.AndAlso<T>(second, Expression.AndAlso);    }    public static Expression<Func<T, bool>> Or<T>(        this Expression<Func<T, bool>> first,         Expression<Func<T, bool>> second)    {        return first.AndAlso<T>(second, Expression.OrElse);    }    private static Expression<Func<T, bool>> AndAlso<T>(    this Expression<Func<T, bool>> expr1,    Expression<Func<T, bool>> expr2,    Func<Expression, Expression, BinaryExpression> func)    {        var parameter = Expression.Parameter(typeof(T));        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);        var left = leftVisitor.Visit(expr1.Body);        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);        var right = rightVisitor.Visit(expr2.Body);        return Expression.Lambda<Func<T, bool>>(            func(left, right), parameter);    }    private class ReplaceExpressionVisitor        : ExpressionVisitor    {        private readonly Expression _oldValue;        private readonly Expression _newValue;        public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)        {            _oldValue = oldValue;            _newValue = newValue;        }        public override Expression Visit(Expression node)        {            if (node == _oldValue)                return _newValue;            return base.Visit(node);        }    }}

.NET Core中合并Expression<Func<T,bool>>的正确姿势