-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Additional refactoring of Null Semantics:
- moving NullSemantics visitor after 2nd level cache - we need to know the parameter values to properly handle IN expressions wrt null semantics, - NullSemantics visitor needs to go before SqlExpressionOptimizer and SearchCondition, so those two are also moved after 2nd level cache, - combining NullSemantics with SqlExpressionOptimizer (kept SqlExpressionOptimizer name) - SearchCondition now applies some relevant optimization itself, so that we don't need to run full optimizer afterwards, - merging InExpressionValuesExpandingExpressionVisitor into SqlExpressionOptimizer as well, so that we don't apply the rewrite for UseRelationalNulls, - preventing NulSemantics from performing double visitation when computing non-nullable columns. Resolves #11464 Resolves #15722 Resolves #18338 Resolves #18597 Resolves #18689 Resolves #19019
- Loading branch information
Showing
30 changed files
with
2,294 additions
and
484 deletions.
There are no files selected for viewing
134 changes: 134 additions & 0 deletions
134
src/EFCore.Relational/Query/FromSqlParameterApplyingExpressionVisitor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Data.Common; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq.Expressions; | ||
using Microsoft.EntityFrameworkCore.Internal; | ||
using Microsoft.EntityFrameworkCore.Query.SqlExpressions; | ||
using Microsoft.EntityFrameworkCore.Storage; | ||
using Microsoft.EntityFrameworkCore.Storage.Internal; | ||
using Microsoft.EntityFrameworkCore.Utilities; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query | ||
{ | ||
public class FromSqlParameterApplyingExpressionVisitor : ExpressionVisitor | ||
{ | ||
private readonly IDictionary<FromSqlExpression, Expression> _visitedFromSqlExpressions | ||
= new Dictionary<FromSqlExpression, Expression>(ReferenceEqualityComparer.Instance); | ||
|
||
private readonly ISqlExpressionFactory _sqlExpressionFactory; | ||
private readonly ParameterNameGenerator _parameterNameGenerator; | ||
private readonly IReadOnlyDictionary<string, object> _parametersValues; | ||
|
||
public FromSqlParameterApplyingExpressionVisitor( | ||
[NotNull] ISqlExpressionFactory sqlExpressionFactory, | ||
[NotNull] ParameterNameGenerator parameterNameGenerator, | ||
[NotNull] IReadOnlyDictionary<string, object> parametersValues) | ||
{ | ||
Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory)); | ||
Check.NotNull(parameterNameGenerator, nameof(parameterNameGenerator)); | ||
Check.NotNull(parametersValues, nameof(parametersValues)); | ||
|
||
_sqlExpressionFactory = sqlExpressionFactory; | ||
_parameterNameGenerator = parameterNameGenerator; | ||
_parametersValues = parametersValues; | ||
} | ||
|
||
public override Expression Visit(Expression expression) | ||
{ | ||
if (expression is FromSqlExpression fromSql) | ||
{ | ||
if (!_visitedFromSqlExpressions.TryGetValue(fromSql, out var updatedFromSql)) | ||
{ | ||
switch (fromSql.Arguments) | ||
{ | ||
case ParameterExpression parameterExpression: | ||
var parameterValues = (object[])_parametersValues[parameterExpression.Name]; | ||
|
||
var subParameters = new List<IRelationalParameter>(parameterValues.Length); | ||
// ReSharper disable once ForCanBeConvertedToForeach | ||
for (var i = 0; i < parameterValues.Length; i++) | ||
{ | ||
var parameterName = _parameterNameGenerator.GenerateNext(); | ||
if (parameterValues[i] is DbParameter dbParameter) | ||
{ | ||
if (string.IsNullOrEmpty(dbParameter.ParameterName)) | ||
{ | ||
dbParameter.ParameterName = parameterName; | ||
} | ||
else | ||
{ | ||
parameterName = dbParameter.ParameterName; | ||
} | ||
|
||
subParameters.Add(new RawRelationalParameter(parameterName, dbParameter)); | ||
} | ||
else | ||
{ | ||
subParameters.Add( | ||
new TypeMappedRelationalParameter( | ||
parameterName, | ||
parameterName, | ||
_sqlExpressionFactory.GetTypeMappingForValue(parameterValues[i]), | ||
parameterValues[i]?.GetType().IsNullableType())); | ||
} | ||
} | ||
|
||
updatedFromSql = new FromSqlExpression( | ||
fromSql.Sql, | ||
Expression.Constant( | ||
new CompositeRelationalParameter( | ||
parameterExpression.Name, | ||
subParameters)), | ||
fromSql.Alias); | ||
|
||
_visitedFromSqlExpressions[fromSql] = updatedFromSql; | ||
break; | ||
|
||
case ConstantExpression constantExpression: | ||
var existingValues = (object[])constantExpression.Value; | ||
var constantValues = new object[existingValues.Length]; | ||
for (var i = 0; i < existingValues.Length; i++) | ||
{ | ||
var value = existingValues[i]; | ||
if (value is DbParameter dbParameter) | ||
{ | ||
var parameterName = _parameterNameGenerator.GenerateNext(); | ||
if (string.IsNullOrEmpty(dbParameter.ParameterName)) | ||
{ | ||
dbParameter.ParameterName = parameterName; | ||
} | ||
else | ||
{ | ||
parameterName = dbParameter.ParameterName; | ||
} | ||
|
||
constantValues[i] = new RawRelationalParameter(parameterName, dbParameter); | ||
} | ||
else | ||
{ | ||
constantValues[i] = _sqlExpressionFactory.Constant( | ||
value, _sqlExpressionFactory.GetTypeMappingForValue(value)); | ||
} | ||
} | ||
|
||
updatedFromSql = new FromSqlExpression( | ||
fromSql.Sql, | ||
Expression.Constant(constantValues, typeof(object[])), | ||
fromSql.Alias); | ||
|
||
_visitedFromSqlExpressions[fromSql] = updatedFromSql; | ||
break; | ||
} | ||
} | ||
|
||
return updatedFromSql; | ||
} | ||
|
||
return base.Visit(expression); | ||
} | ||
} | ||
} |
Oops, something went wrong.