diff --git a/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs b/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs index 331e414bce5..98ead684f5e 100644 --- a/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs +++ b/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs @@ -23,7 +23,7 @@ public static bool IsOrdinalKeyProperty(this IReadOnlyProperty property) (property.DeclaringType as IEntityType)?.IsOwned() == true, $"Expected {property.DeclaringType.DisplayName()} to be owned."); Check.DebugAssert(property.GetJsonPropertyName().Length == 0, $"Expected {property.Name} to be non-persisted."); - return property.FindContainingPrimaryKey() is IReadOnlyKey { Properties.Count: > 1 } + return property.FindContainingPrimaryKey() is { Properties.Count: > 1 } && !property.IsForeignKey() && property.ClrType == typeof(int) && (property.ValueGenerated & ValueGenerated.OnAdd) != 0; diff --git a/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs b/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs index 5367a3eefc6..69e5153404b 100644 --- a/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs +++ b/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs @@ -52,38 +52,19 @@ public SqlExpressionFactory(ITypeMappingSource typeMappingSource, IModel model) /// [return: NotNullIfNotNull("sqlExpression")] public virtual SqlExpression? ApplyTypeMapping(SqlExpression? sqlExpression, CoreTypeMapping? typeMapping) - { - if (sqlExpression is not { TypeMapping: null }) - { - return sqlExpression; - } - -#pragma warning disable IDE0066 // Convert switch statement to expression - switch (sqlExpression) -#pragma warning restore IDE0066 // Convert switch statement to expression + => sqlExpression switch { - case SqlConditionalExpression sqlConditionalExpression: - return ApplyTypeMappingOnSqlConditional(sqlConditionalExpression, typeMapping); - - case SqlBinaryExpression sqlBinaryExpression: - return ApplyTypeMappingOnSqlBinary(sqlBinaryExpression, typeMapping); - - case SqlUnaryExpression sqlUnaryExpression: - return ApplyTypeMappingOnSqlUnary(sqlUnaryExpression, typeMapping); + null or { TypeMapping: not null } => sqlExpression, - case SqlConstantExpression sqlConstantExpression: - return sqlConstantExpression.ApplyTypeMapping(typeMapping); + SqlConditionalExpression sqlConditionalExpression => ApplyTypeMappingOnSqlConditional(sqlConditionalExpression, typeMapping), + SqlBinaryExpression sqlBinaryExpression => ApplyTypeMappingOnSqlBinary(sqlBinaryExpression, typeMapping), + SqlUnaryExpression sqlUnaryExpression => ApplyTypeMappingOnSqlUnary(sqlUnaryExpression, typeMapping), + SqlConstantExpression sqlConstantExpression => sqlConstantExpression.ApplyTypeMapping(typeMapping), + SqlParameterExpression sqlParameterExpression => sqlParameterExpression.ApplyTypeMapping(typeMapping), + SqlFunctionExpression sqlFunctionExpression => sqlFunctionExpression.ApplyTypeMapping(typeMapping), - case SqlParameterExpression sqlParameterExpression: - return sqlParameterExpression.ApplyTypeMapping(typeMapping); - - case SqlFunctionExpression sqlFunctionExpression: - return sqlFunctionExpression.ApplyTypeMapping(typeMapping); - - default: - return sqlExpression; - } - } + _ => sqlExpression + }; private SqlExpression ApplyTypeMappingOnSqlConditional( SqlConditionalExpression sqlConditionalExpression, diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs index c0cfebbde1b..bc86bdd78c2 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs @@ -28,7 +28,7 @@ public static class RelationalPropertyExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static bool IsOrdinalKeyProperty(this IReadOnlyProperty property) - => property.FindContainingPrimaryKey() is IReadOnlyKey { Properties.Count: > 1 } + => property.FindContainingPrimaryKey() is { Properties.Count: > 1 } && !property.IsForeignKey() && property.ClrType == typeof(int) && property.GetJsonPropertyName() == null; diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 89f0adf385f..2fd2e54731c 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -494,13 +494,13 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent => TranslateAggregateWithSelector(source, selector, QueryableMethods.GetAverageWithoutSelector, throwWhenEmpty: true, resultType); /// - protected override ShapedQueryExpression? TranslateCast(ShapedQueryExpression source, Type resultType) + protected override ShapedQueryExpression TranslateCast(ShapedQueryExpression source, Type resultType) => source.ShaperExpression.Type != resultType ? source.UpdateShaperExpression(Expression.Convert(source.ShaperExpression, resultType)) : source; /// - protected override ShapedQueryExpression? TranslateConcat(ShapedQueryExpression source1, ShapedQueryExpression source2) + protected override ShapedQueryExpression TranslateConcat(ShapedQueryExpression source1, ShapedQueryExpression source2) { ((SelectExpression)source1.QueryExpression).ApplyUnion((SelectExpression)source2.QueryExpression, distinct: false); @@ -598,7 +598,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent } /// - protected override ShapedQueryExpression? TranslateDistinct(ShapedQueryExpression source) + protected override ShapedQueryExpression TranslateDistinct(ShapedQueryExpression source) { var selectExpression = (SelectExpression)source.QueryExpression; if (selectExpression.Orderings.Count > 0 @@ -637,7 +637,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent } /// - protected override ShapedQueryExpression? TranslateExcept(ShapedQueryExpression source1, ShapedQueryExpression source2) + protected override ShapedQueryExpression TranslateExcept(ShapedQueryExpression source1, ShapedQueryExpression source2) { ((SelectExpression)source1.QueryExpression).ApplyExcept((SelectExpression)source2.QueryExpression, distinct: true); @@ -797,7 +797,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent => null; /// - protected override ShapedQueryExpression? TranslateIntersect(ShapedQueryExpression source1, ShapedQueryExpression source2) + protected override ShapedQueryExpression TranslateIntersect(ShapedQueryExpression source1, ShapedQueryExpression source2) { ((SelectExpression)source1.QueryExpression).ApplyIntersect((SelectExpression)source2.QueryExpression, distinct: true); @@ -1230,7 +1230,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } /// - protected override ShapedQueryExpression? TranslateUnion(ShapedQueryExpression source1, ShapedQueryExpression source2) + protected override ShapedQueryExpression TranslateUnion(ShapedQueryExpression source1, ShapedQueryExpression source2) { ((SelectExpression)source1.QueryExpression).ApplyUnion((SelectExpression)source2.QueryExpression, distinct: true); @@ -1927,7 +1927,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp source = methodCallExpression.Arguments[0]; var selectMethodCallExpression = default(MethodCallExpression); - if (source is MethodCallExpression { Method: MethodInfo { IsGenericMethod: true } } sourceMethodCall + if (source is MethodCallExpression { Method.IsGenericMethod: true } sourceMethodCall && sourceMethodCall.Method.GetGenericMethodDefinition() == QueryableMethods.Select) { selectMethodCallExpression = sourceMethodCall; @@ -1935,7 +1935,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } var asQueryableMethodCallExpression = default(MethodCallExpression); - if (source is MethodCallExpression { Method: MethodInfo { IsGenericMethod: true } } maybeAsQueryableMethodCall + if (source is MethodCallExpression { Method.IsGenericMethod: true } maybeAsQueryableMethodCall && maybeAsQueryableMethodCall.Method.GetGenericMethodDefinition() == QueryableMethods.AsQueryable) { asQueryableMethodCallExpression = maybeAsQueryableMethodCall; @@ -1946,7 +1946,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp if (source is JsonQueryExpression jsonQueryExpression) { - var collectionIndexExpression = _sqlTranslator.Translate(methodCallExpression.Arguments[1]!); + var collectionIndexExpression = _sqlTranslator.Translate(methodCallExpression.Arguments[1]); if (collectionIndexExpression == null) { // before we return from failed translation @@ -1960,7 +1960,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp methodCallExpression); } - var newJsonQuery = jsonQueryExpression.BindCollectionElement(collectionIndexExpression!); + var newJsonQuery = jsonQueryExpression.BindCollectionElement(collectionIndexExpression); var entityShaper = new RelationalEntityShaperExpression( jsonQueryExpression.EntityType, @@ -2021,56 +2021,56 @@ static Expression PrepareFailedTranslationResult( static bool IsValidSelectorForJsonArrayElementAccess(Expression expression, JsonQueryExpression baselineJsonQuery) { - // JSON_QUERY($[0]).Property - if (expression is MemberExpression - { - Expression: RelationalEntityShaperExpression { ValueBufferExpression: JsonQueryExpression memberJqe } - } - && JsonQueryExpressionIsRootedIn(memberJqe, baselineJsonQuery)) + switch (expression) { - return true; - } - - // MCNE(JSON_QUERY($[0].Collection)) - // MCNE(JSON_QUERY($[0].Collection).AsQueryable()) - // MCNE(JSON_QUERY($[0].Collection).Select(xx => xx.Includes()) - // MCNE(JSON_QUERY($[0].Collection).AsQueryable().Select(xx => xx.Includes()) - if (expression is MaterializeCollectionNavigationExpression mcne) - { - var subquery = mcne.Subquery; - if (subquery is MethodCallExpression { Method: MethodInfo { IsGenericMethod: true } } selectMethodCall - && selectMethodCall.Method.GetGenericMethodDefinition() == QueryableMethods.Select - && selectMethodCall.Arguments[1].UnwrapLambdaFromQuote() is LambdaExpression selectorLambda - && StripIncludes(selectorLambda.Body) == selectorLambda.Parameters[0]) + // JSON_QUERY($[0]).Property + case MemberExpression + { + Expression: RelationalEntityShaperExpression { ValueBufferExpression: JsonQueryExpression memberJqe } + } + when JsonQueryExpressionIsRootedIn(memberJqe, baselineJsonQuery): { - subquery = selectMethodCall.Arguments[0]; + return true; } - if (subquery is MethodCallExpression { Method: MethodInfo { IsGenericMethod: true } } asQueryableMethodCall - && asQueryableMethodCall.Method.GetGenericMethodDefinition() == QueryableMethods.AsQueryable) + // MCNE(JSON_QUERY($[0].Collection)) + // MCNE(JSON_QUERY($[0].Collection).AsQueryable()) + // MCNE(JSON_QUERY($[0].Collection).Select(xx => xx.Includes()) + // MCNE(JSON_QUERY($[0].Collection).AsQueryable().Select(xx => xx.Includes()) + case MaterializeCollectionNavigationExpression { Subquery: var subquery }: { - subquery = asQueryableMethodCall.Arguments[0]; - } + if (subquery is MethodCallExpression { Method.IsGenericMethod: true } selectMethodCall + && selectMethodCall.Method.GetGenericMethodDefinition() == QueryableMethods.Select + && selectMethodCall.Arguments[1].UnwrapLambdaFromQuote() is LambdaExpression selectorLambda + && StripIncludes(selectorLambda.Body) == selectorLambda.Parameters[0]) + { + subquery = selectMethodCall.Arguments[0]; + } - if (subquery is JsonQueryExpression subqueryJqe - && JsonQueryExpressionIsRootedIn(subqueryJqe, baselineJsonQuery)) - { - return true; + if (subquery is MethodCallExpression { Method.IsGenericMethod: true } asQueryableMethodCall + && asQueryableMethodCall.Method.GetGenericMethodDefinition() == QueryableMethods.AsQueryable) + { + subquery = asQueryableMethodCall.Arguments[0]; + } + + if (subquery is JsonQueryExpression subqueryJqe + && JsonQueryExpressionIsRootedIn(subqueryJqe, baselineJsonQuery)) + { + return true; + } + + goto default; } - } - // JSON_QUERY($[0]).Includes() - // JSON_QUERY($[0].Reference).Includes() - // JSON_QUERY($[0]) - // JSON_QUERY($[0].Reference) - expression = StripIncludes(expression); - if (expression is RelationalEntityShaperExpression { ValueBufferExpression: JsonQueryExpression reseJqe } - && JsonQueryExpressionIsRootedIn(reseJqe, baselineJsonQuery)) - { - return true; + default: + // JSON_QUERY($[0]).Includes() + // JSON_QUERY($[0].Reference).Includes() + // JSON_QUERY($[0]) + // JSON_QUERY($[0].Reference) + expression = StripIncludes(expression); + return expression is RelationalEntityShaperExpression { ValueBufferExpression: JsonQueryExpression reseJqe } + && JsonQueryExpressionIsRootedIn(reseJqe, baselineJsonQuery); } - - return false; } static bool JsonQueryExpressionIsRootedIn(JsonQueryExpression expressionToTest, JsonQueryExpression root) @@ -2324,20 +2324,6 @@ private static Expression AccessField( string fieldName) => Expression.Field(targetExpression, transparentIdentifierType.GetTypeInfo().GetDeclaredField(fieldName)!); - private static void HandleGroupByForAggregate(SelectExpression selectExpression, bool eraseProjection = false) - { - if (selectExpression.GroupBy.Count > 0) - { - if (eraseProjection) - { - // Erasing client projections erase projectionMapping projections too - selectExpression.ReplaceProjection(new List()); - } - - selectExpression.PushdownIntoSubquery(); - } - } - private static Expression MatchShaperNullabilityForSetOperation(Expression shaper1, Expression shaper2, bool makeNullable) { switch (shaper1) diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs index 9205bbab608..e2aefc6343d 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs @@ -112,9 +112,7 @@ private static TValue ThrowReadValueException( } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static TValue ThrowExtractJsonPropertyException( - Exception exception, - IProperty property) + private static TValue ThrowExtractJsonPropertyException(Exception exception, IProperty property) { var entityType = property.DeclaringType.DisplayName(); var propertyName = property.Name; diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs index 01aee509205..0bc92ae317b 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs @@ -408,58 +408,64 @@ public LambdaExpression ProcessShaper( protected override Expression VisitBinary(BinaryExpression binaryExpression) { - if (binaryExpression is { NodeType: ExpressionType.Assign, Left: ParameterExpression parameterExpression } - && parameterExpression.Type == typeof(MaterializationContext)) + switch (binaryExpression) { - var newExpression = (NewExpression)binaryExpression.Right; - - if (newExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression) + case { NodeType: ExpressionType.Assign, Left: ParameterExpression parameterExpression } + when parameterExpression.Type == typeof(MaterializationContext): { - var propertyMap = (IDictionary)GetProjectionIndex(projectionBindingExpression); - _materializationContextBindings[parameterExpression] = propertyMap; - _entityTypeIdentifyingExpressionInfo[parameterExpression] = - // If single entity type is being selected in hierarchy then we use the value directly else we store the offset to - // read discriminator value. - _singleEntityTypeDiscriminatorValues.TryGetValue(projectionBindingExpression, out var value) - ? value - : propertyMap.Values.Max() + 1; - - var updatedExpression = newExpression.Update( - new[] { Constant(ValueBuffer.Empty), newExpression.Arguments[1] }); - - return Assign(binaryExpression.Left, updatedExpression); - } + var newExpression = (NewExpression)binaryExpression.Right; - if (newExpression.Arguments[0] is ParameterExpression valueBufferParameter - && _jsonValueBufferToJsonReaderDataAndKeyValuesParameterMapping.ContainsKey(valueBufferParameter)) - { - _jsonMaterializationContextToJsonReaderDataAndKeyValuesParameterMapping[parameterExpression] = - _jsonValueBufferToJsonReaderDataAndKeyValuesParameterMapping[valueBufferParameter]; + if (newExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression) + { + var propertyMap = (IDictionary)GetProjectionIndex(projectionBindingExpression); + _materializationContextBindings[parameterExpression] = propertyMap; + _entityTypeIdentifyingExpressionInfo[parameterExpression] = + // If single entity type is being selected in hierarchy then we use the value directly else we store the offset + // to read discriminator value. + _singleEntityTypeDiscriminatorValues.TryGetValue(projectionBindingExpression, out var value) + ? value + : propertyMap.Values.Max() + 1; + + var updatedExpression = newExpression.Update( + new[] { Constant(ValueBuffer.Empty), newExpression.Arguments[1] }); + + return Assign(binaryExpression.Left, updatedExpression); + } - var updatedExpression = newExpression.Update( - new[] { Constant(ValueBuffer.Empty), newExpression.Arguments[1] }); + if (newExpression.Arguments[0] is ParameterExpression valueBufferParameter + && _jsonValueBufferToJsonReaderDataAndKeyValuesParameterMapping.TryGetValue( + valueBufferParameter, out var mappedParameter)) + { + _jsonMaterializationContextToJsonReaderDataAndKeyValuesParameterMapping[parameterExpression] = mappedParameter; + + var updatedExpression = newExpression.Update( + new[] { Constant(ValueBuffer.Empty), newExpression.Arguments[1] }); + + return Assign(binaryExpression.Left, updatedExpression); + } - return Assign(binaryExpression.Left, updatedExpression); + break; } - } - if (binaryExpression is + case { NodeType: ExpressionType.Assign, Left: MemberExpression { Member: FieldInfo { IsInitOnly: true } } memberExpression - }) - { - return memberExpression.Assign(Visit(binaryExpression.Right)); - } + }: + { + return memberExpression.Assign(Visit(binaryExpression.Right)); + } - // we only have mapping between MaterializationContext and JsonReaderData, but we use JsonReaderManager to extract JSON values - // so we need to add mapping between JsonReaderData and JsonReaderManager parameter, so we know which parameter to use - // when generating actual Get* method - if (binaryExpression is { NodeType: ExpressionType.Assign, Left: ParameterExpression jsonReaderManagerParameter } - && jsonReaderManagerParameter.Type == typeof(Utf8JsonReaderManager)) - { - var jsonReaderDataParameter = (ParameterExpression)((NewExpression)binaryExpression.Right).Arguments[0]; - _jsonReaderDataToJsonReaderManagerParameterMapping[jsonReaderDataParameter] = jsonReaderManagerParameter; + // we only have mapping between MaterializationContext and JsonReaderData, but we use JsonReaderManager to extract JSON + // values so we need to add mapping between JsonReaderData and JsonReaderManager parameter, so we know which parameter to + // use when generating actual Get* method + case { NodeType: ExpressionType.Assign, Left: ParameterExpression jsonReaderManagerParameter } + when jsonReaderManagerParameter.Type == typeof(Utf8JsonReaderManager): + { + var jsonReaderDataParameter = (ParameterExpression)((NewExpression)binaryExpression.Right).Arguments[0]; + _jsonReaderDataToJsonReaderManagerParameterMapping[jsonReaderDataParameter] = jsonReaderManagerParameter; + break; + } } return base.VisitBinary(binaryExpression); @@ -1082,10 +1088,8 @@ Expression CompensateForCollectionMaterialization(ParameterExpression parameter, Constant(_valuesArrayInitializers.Count - 1)), resultType); } - else - { - return parameter; - } + + return parameter; } } @@ -1099,9 +1103,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var property = methodCallExpression.Arguments[2].GetConstantValue(); var mappingParameter = (ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object!; - if (_jsonMaterializationContextToJsonReaderDataAndKeyValuesParameterMapping.ContainsKey(mappingParameter)) + if (_jsonMaterializationContextToJsonReaderDataAndKeyValuesParameterMapping.TryGetValue( + mappingParameter, out var mappedParameter)) { - var (jsonReaderDataParameter, keyPropertyValuesParameter) = _jsonMaterializationContextToJsonReaderDataAndKeyValuesParameterMapping[mappingParameter]; + var (jsonReaderDataParameter, keyPropertyValuesParameter) = mappedParameter; if (property!.IsPrimaryKey()) { @@ -1110,16 +1115,14 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp ObjectArrayIndexerPropertyInfo, new[] { Constant(index) }); } - else - { - var jsonReaderManagerParameter = _jsonReaderDataToJsonReaderManagerParameterMapping[jsonReaderDataParameter]; - var jsonReadPropertyValueExpression = CreateReadJsonPropertyValueExpression(jsonReaderManagerParameter, property); + var jsonReaderManagerParameter = _jsonReaderDataToJsonReaderManagerParameterMapping[jsonReaderDataParameter]; - return methodCallExpression.Type != jsonReadPropertyValueExpression.Type - ? Convert(jsonReadPropertyValueExpression, methodCallExpression.Type) - : jsonReadPropertyValueExpression; - } + var jsonReadPropertyValueExpression = CreateReadJsonPropertyValueExpression(jsonReaderManagerParameter, property); + + return methodCallExpression.Type != jsonReadPropertyValueExpression.Type + ? Convert(jsonReadPropertyValueExpression, methodCallExpression.Type) + : jsonReadPropertyValueExpression; } int projectionIndex; @@ -1625,21 +1628,10 @@ protected override Expression VisitSwitch(SwitchExpression switchExpression) _navigationVariableMap[innerShaperMapElement.Key] = propertyVariable; - var moveNext = Call( - managerVariable, - Utf8JsonReaderManagerMoveNextMethod); - - var captureState = Call( - managerVariable, - Utf8JsonReaderManagerCaptureStateMethod); - - var assignment = Assign( - propertyVariable, - innerShaperMapElement.Value); - - var managerRecreation = Assign( - managerVariable, - New(JsonReaderManagerConstructor, _jsonReaderDataParameter)); + var moveNext = Call(managerVariable, Utf8JsonReaderManagerMoveNextMethod); + var captureState = Call(managerVariable, Utf8JsonReaderManagerCaptureStateMethod); + var assignment = Assign(propertyVariable, innerShaperMapElement.Value); + var managerRecreation = Assign(managerVariable, New(JsonReaderManagerConstructor, _jsonReaderDataParameter)); readExpressions.Add( Block( @@ -1664,29 +1656,20 @@ protected override Expression VisitSwitch(SwitchExpression switchExpression) } var loopBody = Block( - Assign( - tokenTypeVariable, - Call( - managerVariable, - Utf8JsonReaderManagerMoveNextMethod)), + Assign(tokenTypeVariable, Call(managerVariable, Utf8JsonReaderManagerMoveNextMethod)), Switch( tokenTypeVariable, Block( Call( - Field( - managerVariable, - Utf8JsonReaderManagerCurrentReaderField), + Field(managerVariable, Utf8JsonReaderManagerCurrentReaderField), Utf8JsonReaderTrySkipMethod), Default(typeof(void))), - new SwitchCase[] - { - SwitchCase( - testExpression, - Constant(JsonTokenType.PropertyName)), - SwitchCase( - Break(breakLabel), - Constant(JsonTokenType.EndObject)), - })); + SwitchCase( + testExpression, + Constant(JsonTokenType.PropertyName)), + SwitchCase( + Break(breakLabel), + Constant(JsonTokenType.EndObject)))); return (Loop(loopBody, breakLabel), propertyAssignmentMap); } @@ -1710,10 +1693,15 @@ protected override Expression VisitConditional(ConditionalExpression conditional if (_isTracking && visited is ConditionalExpression { - Test: BinaryExpression { NodeType: ExpressionType.NotEqual, Left: ParameterExpression leftPrm, Right: DefaultExpression rightDefault } testBinaryExpression, + Test: BinaryExpression + { + NodeType: ExpressionType.NotEqual, + Left: ParameterExpression, + Right: DefaultExpression rightDefault + } testBinaryExpression, IfTrue: BlockExpression ifTrueBlock, IfFalse: BlockExpression ifFalseBlock - } resultConditional + } && rightDefault.Type == typeof(InternalEntityEntry)) { var entityAlreadyTrackedVariable = Variable(typeof(bool), "entityAlreadyTracked"); @@ -1742,7 +1730,9 @@ protected override Expression VisitConditional(ConditionalExpression conditional resultBlockVariables.AddRange(ifFalseBlock.Variables.ToList()); - var instanceAssignment = ifFalseBlock.Expressions.OfType().Single(e => e is { NodeType: ExpressionType.Assign, Left: ParameterExpression instance, Right: BlockExpression } && instance.Type == _entityType.ClrType); + var instanceAssignment = ifFalseBlock.Expressions.OfType().Single( + e => e is { NodeType: ExpressionType.Assign, Left: ParameterExpression instance, Right: BlockExpression } + && instance.Type == _entityType.ClrType); var instanceAssignmentBody = (BlockExpression)instanceAssignment.Right; var newInstanceAssignmentVariables = instanceAssignmentBody.Variables.ToList(); @@ -1801,22 +1791,21 @@ protected override Expression VisitConditional(ConditionalExpression conditional var newInstanceAssignmentBlock = Block(newInstanceAssignmentVariables, newInstanceAssignmentExpressions); resultBlockExpressions.Add( - Assign( - instanceAssignment.Left, - newInstanceAssignmentBlock)); + Assign(instanceAssignment.Left, newInstanceAssignmentBlock)); var startTrackingAssignment = ifFalseBlock.Expressions .OfType() .Single(e => e is { NodeType: ExpressionType.Assign, Left: ParameterExpression instance, Right: ConditionalExpression } && instance.Type == typeof(InternalEntityEntry)); - var startTrackingExpression = IfThen( - Not( - OrElse( - entityAlreadyTrackedVariable, - ((ConditionalExpression)startTrackingAssignment.Right).Test)), - Block( - ((ConditionalExpression)startTrackingAssignment.Right).IfFalse, - Default(typeof(void)))); + var startTrackingExpression = + IfThen( + Not( + OrElse( + entityAlreadyTrackedVariable, + ((ConditionalExpression)startTrackingAssignment.Right).Test)), + Block( + ((ConditionalExpression)startTrackingAssignment.Right).IfFalse, + Default(typeof(void)))); resultBlockExpressions.Add(startTrackingExpression); resultBlockExpressions.Add(Default(typeof(void))); @@ -1915,9 +1904,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var jsonReaderDataAssignment = Assign( jsonReaderDataVariable, Condition( - Equal( - jsonStreamVariable, - Default(typeof(MemoryStream))), + Equal(jsonStreamVariable, Default(typeof(MemoryStream))), Default(typeof(JsonReaderData)), New(JsonReaderDataConstructor, jsonStreamVariable))); @@ -1927,20 +1914,15 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp // jsonReaderManager.MoveNext(); // jsonReaderManager.CaptureState(); // } - var jsonReaderManagerBlock = IfThen( - NotEqual( - jsonReaderDataVariable, - Default(typeof(JsonReaderData))), - Block( - Assign( - jsonReaderManagerVariable, - New(JsonReaderManagerConstructor, jsonReaderDataVariable)), - Call( - jsonReaderManagerVariable, - Utf8JsonReaderManagerMoveNextMethod), - Call( - jsonReaderManagerVariable, - Utf8JsonReaderManagerCaptureStateMethod))); + var jsonReaderManagerBlock = + IfThen( + NotEqual( + jsonReaderDataVariable, + Default(typeof(JsonReaderData))), + Block( + Assign(jsonReaderManagerVariable, New(JsonReaderManagerConstructor, jsonReaderDataVariable)), + Call(jsonReaderManagerVariable, Utf8JsonReaderManagerMoveNextMethod), + Call(jsonReaderManagerVariable, Utf8JsonReaderManagerCaptureStateMethod))); _variables.Add(jsonStreamVariable); _variables.Add(jsonReaderDataVariable); @@ -1962,6 +1944,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp Constant(constant + 1), typeof(object)); break; + case { KeyProperty: IProperty keyProperty }: // if key value has IProperty, it must be a PK of the owner var projection = _selectExpression.Projection[keyAccessInfo.KeyProjectionIndex!.Value]; @@ -1975,6 +1958,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp keyProperty), typeof(object)); break; + default: // otherwise it must be non-constant array access and we stored its projection index // extract the value from the projection (or the cache if we used it before) @@ -2279,22 +2263,6 @@ private Expression CreateReadJsonPropertyValueExpression( { if (!property.IsNullable || converter.ConvertsNulls) { - // in case of null value we can't just use the JsonReader method, but rather check the current token type - // if it's JsonTokenType.Null means value is null, only if it's not we are safe to read the value - if (nullable) - { - resultExpression = Condition( - Equal( - Property( - Field( - jsonReaderManagerParameter, - Utf8JsonReaderManagerCurrentReaderField), - Utf8JsonReaderTokenTypeProperty), - Constant(JsonTokenType.Null)), - Default(providerClrType), - resultExpression); - } - resultExpression = Convert( Call(jsonReaderWriterExpression, fromJsonMethod, jsonReaderManagerParameter), providerClrType); @@ -2330,10 +2298,8 @@ private Expression CreateReadJsonPropertyValueExpression( resultExpression = Condition( Equal( Property( - Field( - jsonReaderManagerParameter, - Utf8JsonReaderManagerCurrentReaderField), - Utf8JsonReaderTokenTypeProperty), + Field(jsonReaderManagerParameter, Utf8JsonReaderManagerCurrentReaderField), + Utf8JsonReaderTokenTypeProperty), Constant(JsonTokenType.Null)), Default(property.ClrType), resultExpression); @@ -2354,9 +2320,7 @@ private Expression CreateReadJsonPropertyValueExpression( resultExpression = Condition( Equal( Property( - Field( - jsonReaderManagerParameter, - Utf8JsonReaderManagerCurrentReaderField), + Field(jsonReaderManagerParameter, Utf8JsonReaderManagerCurrentReaderField), Utf8JsonReaderTokenTypeProperty), Constant(JsonTokenType.Null)), Default(property.ClrType), diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerTemporalConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerTemporalConvention.cs index be5829edd73..722eca989e2 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerTemporalConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerTemporalConvention.cs @@ -70,7 +70,7 @@ public virtual void ProcessEntityTypeAnnotationChanged( if (skipLevelNavigation.DeclaringEntityType.IsTemporal() && skipLevelNavigation.Inverse is IConventionSkipNavigation inverse && inverse.DeclaringEntityType.IsTemporal() - && skipLevelNavigation.JoinEntityType is IConventionEntityType { HasSharedClrType: true } joinEntityType + && skipLevelNavigation.JoinEntityType is { HasSharedClrType: true } joinEntityType && !joinEntityType.IsTemporal() && joinEntityType.GetConfigurationSource() == ConfigurationSource.Convention) { diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs index c660f7d2600..e3b99f42894 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs @@ -142,7 +142,7 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) /// protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { - if (methodCallExpression is MethodCallExpression { Method: MethodInfo { IsGenericMethod: true } } genericMethodCall + if (methodCallExpression is { Method: { IsGenericMethod: true } } genericMethodCall && genericMethodCall.Method.GetGenericMethodDefinition() == EnumerableMethods.ElementAt && genericMethodCall.Arguments[0].Type == typeof(byte[])) { diff --git a/src/EFCore/Extensions/Internal/TypeExtensions.cs b/src/EFCore/Extensions/Internal/TypeExtensions.cs index 066575aed08..c190e678a28 100644 --- a/src/EFCore/Extensions/Internal/TypeExtensions.cs +++ b/src/EFCore/Extensions/Internal/TypeExtensions.cs @@ -68,7 +68,7 @@ public static string GenerateParameterName(this Type type) pi => pi.Name == defaultPropertyAttribute.MemberName && pi.IsIndexerProperty() - && pi.SetMethod?.GetParameters() is ParameterInfo[] { Length: 2 } parameters + && pi.SetMethod?.GetParameters() is { Length: 2 } parameters && parameters[0].ParameterType == typeof(string)); } } diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index 97fd4cd99e8..505884263cc 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -3141,7 +3141,7 @@ public static InternalIndexBuilder DetachIndex(Index indexToDetach) // TODO: Use convention batch to get the updated builder, see #15898 var principalBuilder = Metadata.IsInModel ? Metadata.Builder - : ownership?.PrincipalEntityType.FindNavigation(ownership.PrincipalToDependent!.Name)?.TargetEntityType is EntityType + : ownership?.PrincipalEntityType.FindNavigation(ownership.PrincipalToDependent!.Name)?.TargetEntityType is { IsInModel: true } target diff --git a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs index 82b21ae77cf..9819a0cd941 100644 --- a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs @@ -87,32 +87,32 @@ protected override Expression VisitExtension(Expression extensionExpression) if (extensionExpression is ShapedQueryExpression shapedQueryExpression) { var serverEnumerable = VisitShapedQuery(shapedQueryExpression); - switch (shapedQueryExpression.ResultCardinality) + + return shapedQueryExpression.ResultCardinality switch { - case ResultCardinality.Enumerable: - return serverEnumerable; - - case ResultCardinality.Single: - return QueryCompilationContext.IsAsync - ? Call( - SingleAsyncMethodInfo.MakeGenericMethod(serverEnumerable.Type.GetSequenceType()), - serverEnumerable, - _cancellationTokenParameter) - : Call( - EnumerableMethods.SingleWithoutPredicate.MakeGenericMethod(serverEnumerable.Type.GetSequenceType()), - serverEnumerable); - - case ResultCardinality.SingleOrDefault: - return QueryCompilationContext.IsAsync - ? Call( - SingleOrDefaultAsyncMethodInfo.MakeGenericMethod(serverEnumerable.Type.GetSequenceType()), - serverEnumerable, - _cancellationTokenParameter) - : Call( - EnumerableMethods.SingleOrDefaultWithoutPredicate.MakeGenericMethod( - serverEnumerable.Type.GetSequenceType()), - serverEnumerable); - } + ResultCardinality.Enumerable => serverEnumerable, + + ResultCardinality.Single => QueryCompilationContext.IsAsync + ? Call( + SingleAsyncMethodInfo.MakeGenericMethod(serverEnumerable.Type.GetSequenceType()), + serverEnumerable, + _cancellationTokenParameter) + : Call( + EnumerableMethods.SingleWithoutPredicate.MakeGenericMethod(serverEnumerable.Type.GetSequenceType()), + serverEnumerable), + + ResultCardinality.SingleOrDefault => QueryCompilationContext.IsAsync + ? Call( + SingleOrDefaultAsyncMethodInfo.MakeGenericMethod(serverEnumerable.Type.GetSequenceType()), + serverEnumerable, + _cancellationTokenParameter) + : Call( + EnumerableMethods.SingleOrDefaultWithoutPredicate.MakeGenericMethod( + serverEnumerable.Type.GetSequenceType()), + serverEnumerable), + + _ => base.VisitExtension(extensionExpression) + }; } return base.VisitExtension(extensionExpression); @@ -356,9 +356,7 @@ private Expression ProcessEntityShaper(EntityShaperExpression entityShaperExpres New( MaterializationContextConstructor, entityShaperExpression.ValueBufferExpression, - MakeMemberAccess( - QueryCompilationContext.QueryContextParameter, - DbContextMemberInfo)))); + MakeMemberAccess(QueryCompilationContext.QueryContextParameter, DbContextMemberInfo)))); var valueBufferExpression = Call(materializationContextVariable, MaterializationContext.GetValueBufferMethod); @@ -371,10 +369,7 @@ private Expression ProcessEntityShaper(EntityShaperExpression entityShaperExpres var instanceVariable = Variable(entityType.ClrType, "instance" + _currentEntityIndex); variables.Add(instanceVariable); - expressions.Add( - Assign( - instanceVariable, - Constant(null, entityType.ClrType))); + expressions.Add(Assign(instanceVariable, Constant(null, entityType.ClrType))); if (_queryStateManager && primaryKey != null) @@ -406,13 +401,9 @@ private Expression ProcessEntityShaper(EntityShaperExpression entityShaperExpres IfThen( Not(hasNullKeyVariable), IfThenElse( - NotEqual( - entryVariable, - Default(typeof(InternalEntityEntry))), + NotEqual(entryVariable, Default(typeof(InternalEntityEntry))), Block( - Assign( - concreteEntityTypeVariable, - MakeMemberAccess(entryVariable, EntityTypeMemberInfo)), + Assign(concreteEntityTypeVariable, MakeMemberAccess(entryVariable, EntityTypeMemberInfo)), Assign( instanceVariable, Convert( MakeMemberAccess(entryVariable, EntityMemberInfo), diff --git a/test/EFCore.Relational.Specification.Tests/Query/OperatorsProceduralQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/OperatorsProceduralQueryTestBase.cs index 0aeb301009e..3f01f8007dc 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/OperatorsProceduralQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/OperatorsProceduralQueryTestBase.cs @@ -677,7 +677,7 @@ public ResultExpressionProjectionRewriter(Expression resultExpression, Expressio protected override Expression VisitNew(NewExpression newExpression) { - if (newExpression.Constructor is ConstructorInfo { DeclaringType: Type { IsGenericType: true } declaringType }) + if (newExpression.Constructor is { DeclaringType: { IsGenericType: true } declaringType }) { if (declaringType.GetGenericTypeDefinition() == typeof(OperatorDto1<,>)) { diff --git a/test/EFCore.Specification.Tests/Query/InheritanceQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/InheritanceQueryTestBase.cs index ed5ebd3ad86..b94bbd02b31 100644 --- a/test/EFCore.Specification.Tests/Query/InheritanceQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/InheritanceQueryTestBase.cs @@ -74,6 +74,7 @@ public virtual Task Can_use_backwards_is_animal(bool async) => AssertQuery( async, // ReSharper disable once IsExpressionAlwaysTrue + // ReSharper disable once ConvertTypeCheckToNullCheck ss => ss.Set().Where(a => a is Animal), entryCount: 1); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindAggregateOperatorsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindAggregateOperatorsQueryTestBase.cs index b4304117daa..cfc37c23e2d 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindAggregateOperatorsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindAggregateOperatorsQueryTestBase.cs @@ -1776,6 +1776,7 @@ public virtual Task Count_after_client_projection(bool async) => AssertCount( async, ss => ss.Set() + // ReSharper disable once ConvertTypeCheckToNullCheck .Select(o => new { o.OrderID, Customer = o.Customer is Customer ? new { o.Customer.ContactName } : null }) .Take(1)); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs index 1c2c568ccce..320ddeaa8bd 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs @@ -1641,6 +1641,7 @@ public virtual Task Where_projection(bool async) public virtual Task Where_Is_on_same_type(bool async) => AssertQuery( async, + // ReSharper disable once ConvertTypeCheckToNullCheck ss => ss.Set().Where(c => c is Customer), entryCount: 91); @@ -1760,6 +1761,8 @@ public virtual Task Decimal_cast_to_double_works(bool async) public virtual Task Where_is_conditional(bool async) => AssertQuery( async, + // ReSharper disable once ConvertTypeCheckToNullCheck + // ReSharper disable once SimplifyConditionalTernaryExpression ss => ss.Set().Where(p => p is Product ? false : true)); [ConditionalTheory] diff --git a/test/EFCore.Specification.Tests/TestUtilities/ExpectedQueryRewritingVisitor.cs b/test/EFCore.Specification.Tests/TestUtilities/ExpectedQueryRewritingVisitor.cs index 8baebd92a27..29b9aa43db5 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/ExpectedQueryRewritingVisitor.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/ExpectedQueryRewritingVisitor.cs @@ -246,7 +246,7 @@ private Expression TryConvertEFPropertyToMemberAccess(Expression expression) var methodInfo = _getShadowPropertyValueMethodInfo.MakeGenericMethod(caller.Type, methodCallExpression.Type); result = Expression.Call(methodInfo, caller, Expression.Constant(shadowPropertyMapping)); } - else if (caller.Type.GetMembers().SingleOrDefault(m => m.Name == propertyName) is MemberInfo) + else if (caller.Type.GetMembers().SingleOrDefault(m => m.Name == propertyName) is not null) { result = Expression.Property(caller, propertyName); }