@@ -31,6 +31,9 @@ public class ParameterExtractingExpressionVisitor : ExpressionVisitor
3131 private static readonly bool UseOldBehavior31552 =
3232 AppContext . TryGetSwitch ( "Microsoft.EntityFrameworkCore.Issue31552" , out var enabled31552 ) && enabled31552 ;
3333
34+ private static readonly bool UseOldBehavior35100 =
35+ AppContext . TryGetSwitch ( "Microsoft.EntityFrameworkCore.Issue35100" , out var enabled35100 ) && enabled35100 ;
36+
3437 /// <summary>
3538 /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
3639 /// 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
181184 /// </summary>
182185 protected override Expression VisitMethodCall ( MethodCallExpression methodCallExpression )
183186 {
187+ var method = methodCallExpression . Method ;
188+
184189 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 ) )
187192 {
188193 // If this is a call to EF.Constant(), then examine its operand. If the operand isn't evaluatable (i.e. contains a reference
189194 // to a database table), throw immediately.
@@ -197,6 +202,52 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
197202 return Evaluate ( operand , generateParameter : false ) ;
198203 }
199204
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+
200251 return base . VisitMethodCall ( methodCallExpression ) ;
201252 }
202253
0 commit comments