@@ -31,6 +31,9 @@ public class ParameterExtractingExpressionVisitor : ExpressionVisitor
31
31
private static readonly bool UseOldBehavior31552 =
32
32
AppContext . TryGetSwitch ( "Microsoft.EntityFrameworkCore.Issue31552" , out var enabled31552 ) && enabled31552 ;
33
33
34
+ private static readonly bool UseOldBehavior35100 =
35
+ AppContext . TryGetSwitch ( "Microsoft.EntityFrameworkCore.Issue35100" , out var enabled35100 ) && enabled35100 ;
36
+
34
37
/// <summary>
35
38
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
36
39
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -181,9 +184,11 @@ protected override Expression VisitConditional(ConditionalExpression conditional
181
184
/// </summary>
182
185
protected override Expression VisitMethodCall ( MethodCallExpression methodCallExpression )
183
186
{
187
+ var method = methodCallExpression . Method ;
188
+
184
189
if ( ! UseOldBehavior31552
185
- && methodCallExpression . Method . DeclaringType == typeof ( EF )
186
- && methodCallExpression . Method . Name == nameof ( EF . Constant ) )
190
+ && method . DeclaringType == typeof ( EF )
191
+ && method . Name == nameof ( EF . Constant ) )
187
192
{
188
193
// If this is a call to EF.Constant(), then examine its operand. If the operand isn't evaluatable (i.e. contains a reference
189
194
// to a database table), throw immediately.
@@ -197,6 +202,52 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
197
202
return Evaluate ( operand , generateParameter : false ) ;
198
203
}
199
204
205
+ // .NET 10 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans").
206
+ // Unfortunately, the LINQ interpreter does not support ref structs, so we rewrite e.g. MemoryExtensions.Contains to
207
+ // Enumerable.Contains here. See https://github.com/dotnet/runtime/issues/109757.
208
+ if ( method . DeclaringType == typeof ( MemoryExtensions ) && ! UseOldBehavior35100 )
209
+ {
210
+ switch ( method . Name )
211
+ {
212
+ case nameof ( MemoryExtensions . Contains )
213
+ when methodCallExpression . Arguments is [ var arg0 , var arg1 ] &&
214
+ TryUnwrapSpanImplicitCast ( arg0 , out var unwrappedArg0 ) :
215
+ {
216
+ return Visit (
217
+ Expression . Call (
218
+ EnumerableMethods . Contains . MakeGenericMethod ( method . GetGenericArguments ( ) [ 0 ] ) ,
219
+ unwrappedArg0 , arg1 ) ) ;
220
+ }
221
+
222
+ case nameof ( MemoryExtensions . SequenceEqual )
223
+ when methodCallExpression . Arguments is [ var arg0 , var arg1 ]
224
+ && TryUnwrapSpanImplicitCast ( arg0 , out var unwrappedArg0 )
225
+ && TryUnwrapSpanImplicitCast ( arg1 , out var unwrappedArg1 ) :
226
+ return Visit (
227
+ Expression . Call (
228
+ EnumerableMethods . SequenceEqual . MakeGenericMethod ( method . GetGenericArguments ( ) [ 0 ] ) ,
229
+ unwrappedArg0 , unwrappedArg1 ) ) ;
230
+ }
231
+
232
+ static bool TryUnwrapSpanImplicitCast ( Expression expression , [ NotNullWhen ( true ) ] out Expression ? result )
233
+ {
234
+ if ( expression is MethodCallExpression
235
+ {
236
+ Method : { Name : "op_Implicit" , DeclaringType : { IsGenericType : true } implicitCastDeclaringType } ,
237
+ Arguments : [ var unwrapped ]
238
+ }
239
+ && implicitCastDeclaringType . GetGenericTypeDefinition ( ) is var genericTypeDefinition
240
+ && ( genericTypeDefinition == typeof ( Span < > ) || genericTypeDefinition == typeof ( ReadOnlySpan < > ) ) )
241
+ {
242
+ result = unwrapped ;
243
+ return true ;
244
+ }
245
+
246
+ result = null ;
247
+ return false ;
248
+ }
249
+ }
250
+
200
251
return base . VisitMethodCall ( methodCallExpression ) ;
201
252
}
202
253
0 commit comments