@@ -135,13 +135,7 @@ protected override Expression VisitExtension(Expression node)
135
135
{
136
136
// Create parameter for value if we didn't create it yet,
137
137
// otherwise reuse it.
138
- if ( expandedParameters . Count <= i )
139
- {
140
- var parameterName = Uniquifier . Uniquify ( valuesParameter . Name , queryParameters , int . MaxValue ) ;
141
- queryParameters . Add ( parameterName , values [ i ] ) ;
142
- var parameterExpression = new SqlParameterExpression ( parameterName , values [ i ] ? . GetType ( ) ?? typeof ( object ) , elementTypeMapping ) ;
143
- expandedParameters . Add ( parameterExpression ) ;
144
- }
138
+ ExpandParameterIfNeeded ( valuesParameter . Name , expandedParameters , queryParameters , i , values [ i ] , elementTypeMapping ) ;
145
139
146
140
processedValues . Add (
147
141
new RowValueExpression (
@@ -832,13 +826,7 @@ InExpression ProcessInExpressionValues(
832
826
{
833
827
// Create parameter for value if we didn't create it yet,
834
828
// otherwise reuse it.
835
- if ( expandedParameters . Count <= i )
836
- {
837
- var parameterName = Uniquifier . Uniquify ( valuesParameter . Name , parameters , int . MaxValue ) ;
838
- parameters . Add ( parameterName , values [ i ] ) ;
839
- var parameterExpression = new SqlParameterExpression ( parameterName , values [ i ] ? . GetType ( ) ?? typeof ( object ) , elementTypeMapping ) ;
840
- expandedParameters . Add ( parameterExpression ) ;
841
- }
829
+ ExpandParameterIfNeeded ( valuesParameter . Name , expandedParameters , parameters , i , values [ i ] , elementTypeMapping ) ;
842
830
843
831
// Use separate counter, because we may skip nulls.
844
832
processedValues . Add ( expandedParameters [ expandedParametersCounter ++ ] ) ;
@@ -857,6 +845,23 @@ InExpression ProcessInExpressionValues(
857
845
throw new UnreachableException ( ) ;
858
846
}
859
847
}
848
+
849
+ // Bucketization.
850
+ if ( ( valuesParameter . TranslationMode ?? CollectionParameterTranslationMode ) is ParameterTranslationMode . MultipleParameters )
851
+ {
852
+ // For provider to effectively disable bucketization, return always 1 from ParametersPadFactor.
853
+ var padFactor = ParametersPadFactor ( values . Count ) ;
854
+ var padding = ( padFactor - ( values . Count % padFactor ) ) % padFactor ;
855
+ for ( var i = 0 ; i < padding ; i ++ )
856
+ {
857
+ // Create parameter for value if we didn't create it yet,
858
+ // otherwise reuse it.
859
+ ExpandParameterIfNeeded ( valuesParameter . Name , expandedParameters , parameters , values . Count + i , values [ ^ 1 ] , elementTypeMapping ) ;
860
+
861
+ // Use separate counter, because we may skip nulls.
862
+ processedValues . Add ( expandedParameters [ expandedParametersCounter ++ ] ) ;
863
+ }
864
+ }
860
865
}
861
866
else
862
867
{
@@ -1488,6 +1493,21 @@ protected virtual SqlExpression VisitJsonScalar(
1488
1493
protected virtual bool PreferExistsToInWithCoalesce
1489
1494
=> false ;
1490
1495
1496
+ /// <summary>
1497
+ /// Gets the factor by which the parameters are padded when generating a parameterized collection
1498
+ /// when using multiple parameters. This helps with query plan bloat.
1499
+ /// </summary>
1500
+ /// <param name="count">Number of value parameters are generated for.</param>
1501
+ protected virtual int ParametersPadFactor ( int count )
1502
+ => count switch
1503
+ {
1504
+ <= 5 => 1 ,
1505
+ <= 150 => 10 ,
1506
+ <= 750 => 50 ,
1507
+ <= 2000 => 100 ,
1508
+ _ => 200 ,
1509
+ } ;
1510
+
1491
1511
// Note that we can check parameter values for null since we cache by the parameter nullability; but we cannot do the same for bool.
1492
1512
private bool IsNull ( SqlExpression ? expression )
1493
1513
=> expression is SqlConstantExpression { Value : null }
@@ -2121,4 +2141,21 @@ private SqlExpression ProcessNullNotNull(SqlExpression sqlExpression, bool opera
2121
2141
2122
2142
private static bool IsLogicalNot ( SqlUnaryExpression ? sqlUnaryExpression )
2123
2143
=> sqlUnaryExpression is { OperatorType : ExpressionType . Not } && sqlUnaryExpression . Type == typeof ( bool ) ;
2144
+
2145
+ private static void ExpandParameterIfNeeded (
2146
+ string valuesParameterName ,
2147
+ List < SqlParameterExpression > expandedParameters ,
2148
+ Dictionary < string , object ? > parameters ,
2149
+ int index ,
2150
+ object ? value ,
2151
+ RelationalTypeMapping typeMapping )
2152
+ {
2153
+ if ( expandedParameters . Count <= index )
2154
+ {
2155
+ var parameterName = Uniquifier . Uniquify ( valuesParameterName , parameters , int . MaxValue ) ;
2156
+ parameters . Add ( parameterName , value ) ;
2157
+ var parameterExpression = new SqlParameterExpression ( parameterName , value ? . GetType ( ) ?? typeof ( object ) , typeMapping ) ;
2158
+ expandedParameters . Add ( parameterExpression ) ;
2159
+ }
2160
+ }
2124
2161
}
0 commit comments