@@ -105,13 +105,35 @@ void IPrintableExpression.Print(ExpressionPrinter expressionPrinter)
105
105
106
106
private readonly struct CommandCacheKey : IEquatable < CommandCacheKey >
107
107
{
108
+ private static readonly bool UseOldBehavior34201 =
109
+ AppContext . TryGetSwitch ( "Microsoft.EntityFrameworkCore.Issue34028" , out var enabled34028 ) && enabled34028 ;
110
+
108
111
private readonly Expression _queryExpression ;
109
- private readonly IReadOnlyDictionary < string , object ? > _parameterValues ;
112
+ private readonly Dictionary < string , ParameterInfo > _parameterInfos ;
113
+
114
+ // Quirk only
115
+ private readonly IReadOnlyDictionary < string , object ? > ? _parameterValues ;
110
116
111
117
public CommandCacheKey ( Expression queryExpression , IReadOnlyDictionary < string , object ? > parameterValues )
112
118
{
113
119
_queryExpression = queryExpression ;
114
- _parameterValues = parameterValues ;
120
+ _parameterInfos = new Dictionary < string , ParameterInfo > ( ) ;
121
+
122
+ if ( UseOldBehavior34201 )
123
+ {
124
+ _parameterValues = parameterValues ;
125
+ }
126
+ else
127
+ {
128
+ foreach ( var ( key , value ) in parameterValues )
129
+ {
130
+ _parameterInfos [ key ] = new ParameterInfo
131
+ {
132
+ IsNull = value is null ,
133
+ ObjectArrayLength = value is object [ ] arr ? arr . Length : null
134
+ } ;
135
+ }
136
+ }
115
137
}
116
138
117
139
public override bool Equals ( object ? obj )
@@ -126,26 +148,43 @@ public bool Equals(CommandCacheKey commandCacheKey)
126
148
return false ;
127
149
}
128
150
129
- if ( _parameterValues . Count > 0 )
151
+ Check . DebugAssert (
152
+ _parameterInfos . Count == commandCacheKey . _parameterInfos . Count ,
153
+ "Parameter Count mismatch between identical queries" ) ;
154
+
155
+ if ( _parameterInfos . Count > 0 )
130
156
{
131
- foreach ( var ( key , value ) in _parameterValues )
157
+ if ( UseOldBehavior34201 )
132
158
{
133
- if ( ! commandCacheKey . _parameterValues . TryGetValue ( key , out var otherValue ) )
134
- {
135
- return false ;
136
- }
137
-
138
- // ReSharper disable once ArrangeRedundantParentheses
139
- if ( ( value == null ) != ( otherValue == null ) )
159
+ foreach ( var ( key , value ) in _parameterValues ! )
140
160
{
141
- return false ;
161
+ if ( ! _parameterValues . TryGetValue ( key , out var otherValue ) )
162
+ {
163
+ return false ;
164
+ }
165
+
166
+ // ReSharper disable once ArrangeRedundantParentheses
167
+ if ( ( value == null ) != ( otherValue == null ) )
168
+ {
169
+ return false ;
170
+ }
171
+
172
+ if ( value is IEnumerable
173
+ && value . GetType ( ) == typeof ( object [ ] ) )
174
+ {
175
+ // FromSql parameters must have the same number of elements
176
+ return ( ( object [ ] ) value ) . Length == ( otherValue as object [ ] ) ? . Length ;
177
+ }
142
178
}
143
-
144
- if ( value is IEnumerable
145
- && value . GetType ( ) == typeof ( object [ ] ) )
179
+ }
180
+ else
181
+ {
182
+ foreach ( var ( key , info ) in _parameterInfos )
146
183
{
147
- // FromSql parameters must have the same number of elements
148
- return ( ( object [ ] ) value ) . Length == ( otherValue as object [ ] ) ? . Length ;
184
+ if ( ! commandCacheKey . _parameterInfos . TryGetValue ( key , out var otherInfo ) || info != otherInfo )
185
+ {
186
+ return false ;
187
+ }
149
188
}
150
189
}
151
190
}
@@ -156,4 +195,8 @@ public bool Equals(CommandCacheKey commandCacheKey)
156
195
public override int GetHashCode ( )
157
196
=> 0 ;
158
197
}
198
+
199
+ // Note that we keep only the null-ness of parameters (and array length for FromSql object arrays),
200
+ // and avoid referencing the actual parameter data (see #34028).
201
+ private readonly record struct ParameterInfo ( bool IsNull , int ? ObjectArrayLength ) ;
159
202
}
0 commit comments