private SqlExpression SimplifyUnaryExpression( ExpressionType operatorType, SqlExpression operand, Type type, RelationalTypeMapping typeMapping) { switch (operatorType) { case ExpressionType.Not when type == typeof(bool): { switch (operand) { // !(true) -> false // !(false) -> true case SqlConstantExpression constantOperand when constantOperand.Value is bool value: { return(_sqlExpressionFactory.Constant(!value, typeMapping)); } case InExpression inOperand: return(inOperand.Negate()); case SqlUnaryExpression unaryOperand: switch (unaryOperand.OperatorType) { // !(!a) -> a case ExpressionType.Not: return(unaryOperand.Operand); //!(a IS NULL) -> a IS NOT NULL case ExpressionType.Equal: return(_sqlExpressionFactory.IsNotNull(unaryOperand.Operand)); //!(a IS NOT NULL) -> a IS NULL case ExpressionType.NotEqual: return(_sqlExpressionFactory.IsNull(unaryOperand.Operand)); } break; case SqlBinaryExpression binaryOperand: { // De Morgan's if (binaryOperand.OperatorType == ExpressionType.AndAlso || binaryOperand.OperatorType == ExpressionType.OrElse) { var newLeft = SimplifyUnaryExpression(ExpressionType.Not, binaryOperand.Left, type, typeMapping); var newRight = SimplifyUnaryExpression(ExpressionType.Not, binaryOperand.Right, type, typeMapping); return(SimplifyLogicalSqlBinaryExpression( binaryOperand.OperatorType == ExpressionType.AndAlso ? ExpressionType.OrElse : ExpressionType.AndAlso, newLeft, newRight, binaryOperand.TypeMapping !)); } // those optimizations are only valid in 2-value logic // they are safe to do here because if we apply null semantics // because null semantics removes possibility of nulls in the tree when the comparison is wrapped around NOT if (!_useRelationalNulls && TryNegate(binaryOperand.OperatorType, out var negated)) { return(SimplifyBinaryExpression( negated, binaryOperand.Left, binaryOperand.Right, binaryOperand.TypeMapping !)); } } break; } break; } case ExpressionType.Equal: case ExpressionType.NotEqual: return(SimplifyNullNotNullExpression( operatorType, operand, type, typeMapping)); } return(_sqlExpressionFactory.MakeUnary(operatorType, operand, type, typeMapping) !); }