// ?a == ?b -> [(a == b) && (a != null && b != null)] || (a == null && b == null)) // // a | b | F1 = a == b | F2 = (a != null && b != null) | F3 = F1 && F2 | // | | | | | // 0 | 0 | 1 | 1 | 1 | // 0 | 1 | 0 | 1 | 0 | // 0 | N | N | 0 | 0 | // 1 | 0 | 0 | 1 | 0 | // 1 | 1 | 1 | 1 | 1 | // 1 | N | N | 0 | 0 | // N | 0 | N | 0 | 0 | // N | 1 | N | 0 | 0 | // N | N | N | 0 | 0 | // // a | b | F4 = (a == null && b == null) | Final = F3 OR F4 | // | | | | // 0 | 0 | 0 | 1 OR 0 = 1 | // 0 | 1 | 0 | 0 OR 0 = 0 | // 0 | N | 0 | 0 OR 0 = 0 | // 1 | 0 | 0 | 0 OR 0 = 0 | // 1 | 1 | 0 | 1 OR 0 = 1 | // 1 | N | 0 | 0 OR 0 = 0 | // N | 0 | 0 | 0 OR 0 = 0 | // N | 1 | 0 | 0 OR 0 = 0 | // N | N | 1 | 0 OR 1 = 1 | private SqlBinaryExpression ExpandNullableEqualNullable( SqlExpression left, SqlExpression right, SqlExpression leftIsNull, SqlExpression rightIsNull) => _sqlExpressionFactory.OrElse( _sqlExpressionFactory.AndAlso( _sqlExpressionFactory.Equal(left, right), _sqlExpressionFactory.AndAlso( _sqlExpressionFactory.Not(leftIsNull), _sqlExpressionFactory.Not(rightIsNull))), _sqlExpressionFactory.AndAlso( leftIsNull, rightIsNull));
/// <summary> /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// </summary> protected override Expression VisitUnary(UnaryExpression unaryExpression) { Check.NotNull(unaryExpression, nameof(unaryExpression)); var operand = Visit(unaryExpression.Operand); if (TranslationFailed(unaryExpression.Operand, operand, out var sqlOperand)) { return(null); } switch (unaryExpression.NodeType) { case ExpressionType.Not: return(_sqlExpressionFactory.Not(sqlOperand)); case ExpressionType.Negate: return(_sqlExpressionFactory.Negate(sqlOperand)); case ExpressionType.Convert: // Object convert needs to be converted to explicit cast when mismatching types if (operand.Type.IsInterface && unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type) || unaryExpression.Type.UnwrapNullableType() == operand.Type || unaryExpression.Type.UnwrapNullableType() == typeof(Enum)) { return(sqlOperand); } break; } return(null); }
protected override ShapedQueryExpression TranslateAll(ShapedQueryExpression source, LambdaExpression predicate) { var selectExpression = (SelectExpression)source.QueryExpression; if (selectExpression.Limit != null || selectExpression.Offset != null) { selectExpression.PushdownIntoSubQuery(); } var translation = TranslateLambdaExpression(source, predicate); if (translation != null) { selectExpression.ApplyPredicate(_sqlExpressionFactory.Not(translation)); selectExpression.ReplaceProjection(new Dictionary <ProjectionMember, Expression>()); if (selectExpression.Limit == null && selectExpression.Offset == null) { selectExpression.ClearOrdering(); } translation = _sqlExpressionFactory.Exists(selectExpression, true); source.QueryExpression = _sqlExpressionFactory.Select(translation); source.ShaperExpression = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(bool)); return(source); } throw new InvalidOperationException(); }
protected override Expression VisitUnary(UnaryExpression unaryExpression) { var operand = Visit(unaryExpression.Operand); if (TranslationFailed(unaryExpression.Operand, operand)) { return(null); } var sqlOperand = (SqlExpression)operand; switch (unaryExpression.NodeType) { case ExpressionType.Not: return(_sqlExpressionFactory.Not(sqlOperand)); case ExpressionType.Negate: return(_sqlExpressionFactory.Negate(sqlOperand)); case ExpressionType.Convert: // Object convert needs to be converted to explicit cast when mismatching types if (operand.Type.IsInterface && unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type) || unaryExpression.Type.UnwrapNullableType() == operand.Type || unaryExpression.Type.UnwrapNullableType() == typeof(Enum)) { return(sqlOperand); } sqlOperand = _sqlExpressionFactory.ApplyDefaultTypeMapping(sqlOperand); return(_sqlExpressionFactory.Convert(sqlOperand, unaryExpression.Type)); } return(null); }
protected override Expression VisitUnary(UnaryExpression unaryExpression) { var operand = Visit(unaryExpression.Operand); if (operand is EntityProjectionExpression) { return(unaryExpression.Update(operand)); } if (TranslationFailed(unaryExpression.Operand, operand)) { return(null); } var sqlOperand = (SqlExpression)operand; switch (unaryExpression.NodeType) { case ExpressionType.Not: return(_sqlExpressionFactory.Not(sqlOperand)); case ExpressionType.Negate: return(_sqlExpressionFactory.Negate(sqlOperand)); case ExpressionType.Convert: // Object convert needs to be converted to explicit cast when mismatching types if (operand.Type.IsInterface && unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type) || unaryExpression.Type.UnwrapNullableType() == operand.Type || unaryExpression.Type.UnwrapNullableType() == typeof(Enum)) { return(sqlOperand); } //// Introduce explicit cast only if the target type is mapped else we need to client eval //if (unaryExpression.Type == typeof(object) // || _sqlExpressionFactory.FindMapping(unaryExpression.Type) != null) //{ // sqlOperand = _sqlExpressionFactory.ApplyDefaultTypeMapping(sqlOperand); // return _sqlExpressionFactory.Convert(sqlOperand, unaryExpression.Type); //} break; } return(null); }
//protected override Expression VisitNew(NewExpression newExpression) //{ // if (newExpression.Members == null // || newExpression.Arguments.Count == 0) // { // return null; // } // var bindings = new Expression[newExpression.Arguments.Count]; // for (var i = 0; i < bindings.Length; i++) // { // var translation = Visit(newExpression.Arguments[i]); // if (translation == null) // { // return null; // } // bindings[i] = translation; // } // return Expression.Constant(bindings); //} protected override Expression VisitUnary(UnaryExpression unaryExpression) { var operand = Visit(unaryExpression.Operand); if (TranslationFailed(unaryExpression.Operand, operand)) { return(null); } // In certain cases EF.Property would have convert node around the source. if (operand is EntityShaperExpression && unaryExpression.Type == typeof(object) && unaryExpression.NodeType == ExpressionType.Convert) { return(operand); } var sqlOperand = (SqlExpression)operand; if (unaryExpression.NodeType == ExpressionType.Convert) { // Object convert needs to be converted to explicit cast when mismatching types if (operand.Type.IsInterface && unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type) || unaryExpression.Type.UnwrapNullableType() == operand.Type || unaryExpression.Type.UnwrapNullableType() == typeof(Enum)) { return(sqlOperand); } sqlOperand = _sqlExpressionFactory.ApplyDefaultTypeMapping(sqlOperand); return(_sqlExpressionFactory.Convert(sqlOperand, unaryExpression.Type)); } if (unaryExpression.NodeType == ExpressionType.Not) { return(_sqlExpressionFactory.Not(sqlOperand)); } return(null); }
private Expression VisitSqlUnaryExpression(SqlUnaryExpression sqlUnaryExpression) { // !(true) -> false // !(false) -> true if (sqlUnaryExpression.OperatorType == ExpressionType.Not && sqlUnaryExpression.Operand is SqlConstantExpression innerConstantBool && innerConstantBool.Value is bool value) { return(value ? _sqlExpressionFactory.Constant(false, sqlUnaryExpression.TypeMapping) : _sqlExpressionFactory.Constant(true, sqlUnaryExpression.TypeMapping)); } // NULL IS NULL -> true // non_nullablee_constant IS NULL -> false if (sqlUnaryExpression.OperatorType == ExpressionType.Equal && sqlUnaryExpression.Operand is SqlConstantExpression innerConstantNull1) { return(_sqlExpressionFactory.Constant(innerConstantNull1.Value == null, sqlUnaryExpression.TypeMapping)); } // NULL IS NOT NULL -> false // non_nullablee_constant IS NOT NULL -> true if (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual && sqlUnaryExpression.Operand is SqlConstantExpression innerConstantNull2) { return(_sqlExpressionFactory.Constant(innerConstantNull2.Value != null, sqlUnaryExpression.TypeMapping)); } if (sqlUnaryExpression.Operand is SqlUnaryExpression innerUnary) { if (sqlUnaryExpression.OperatorType == ExpressionType.Not) { // !(!a) -> a if (innerUnary.OperatorType == ExpressionType.Not) { return(Visit(innerUnary.Operand)); } if (innerUnary.OperatorType == ExpressionType.Equal) { //!(a IS NULL) -> a IS NOT NULL return(Visit(_sqlExpressionFactory.IsNotNull(innerUnary.Operand))); } //!(a IS NOT NULL) -> a IS NULL if (innerUnary.OperatorType == ExpressionType.NotEqual) { return(Visit(_sqlExpressionFactory.IsNull(innerUnary.Operand))); } } // (!a) IS NULL <==> a IS NULL if (sqlUnaryExpression.OperatorType == ExpressionType.Equal && innerUnary.OperatorType == ExpressionType.Not) { return(Visit(_sqlExpressionFactory.IsNull(innerUnary.Operand))); } // (!a) IS NOT NULL <==> a IS NOT NULL if (sqlUnaryExpression.OperatorType == ExpressionType.NotEqual && innerUnary.OperatorType == ExpressionType.Not) { return(Visit(_sqlExpressionFactory.IsNotNull(innerUnary.Operand))); } } if (sqlUnaryExpression.Operand is SqlBinaryExpression innerBinary) { // De Morgan's if (innerBinary.OperatorType == ExpressionType.AndAlso || innerBinary.OperatorType == ExpressionType.OrElse) { var newLeft = (SqlExpression)Visit(_sqlExpressionFactory.Not(innerBinary.Left)); var newRight = (SqlExpression)Visit(_sqlExpressionFactory.Not(innerBinary.Right)); return(innerBinary.OperatorType == ExpressionType.AndAlso ? _sqlExpressionFactory.OrElse(newLeft, newRight) : _sqlExpressionFactory.AndAlso(newLeft, newRight)); } // note that those optimizations are only valid in 2-value logic // they are safe to do here because null semantics removes possibility of nulls in the tree // however if we decide to do "partial" null semantics (that doesn't distinguish between NULL and FALSE, e.g. for predicates) // we need to be extra careful here if (TryNegate(innerBinary.OperatorType, out var negated)) { return(Visit( _sqlExpressionFactory.MakeBinary( negated, innerBinary.Left, innerBinary.Right, innerBinary.TypeMapping))); } } var newOperand = (SqlExpression)Visit(sqlUnaryExpression.Operand); return(sqlUnaryExpression.Update(newOperand)); }