diff --git a/.github/workflows/TestCosmos.yaml b/.github/workflows/TestCosmos.yaml
index 52b8aeb3cbd..cb9d7103301 100644
--- a/.github/workflows/TestCosmos.yaml
+++ b/.github/workflows/TestCosmos.yaml
@@ -35,7 +35,7 @@ jobs:
shell: cmd
- name: Publish Test Results
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
diff --git a/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs b/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs
index 558104b7d40..410f2dd2839 100644
--- a/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs
+++ b/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs
@@ -31,6 +31,9 @@ public class ParameterExtractingExpressionVisitor : ExpressionVisitor
private static readonly bool UseOldBehavior31552 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31552", out var enabled31552) && enabled31552;
+ private static readonly bool UseOldBehavior35100 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35100", out var enabled35100) && enabled35100;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// 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
///
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
+ var method = methodCallExpression.Method;
+
if (!UseOldBehavior31552
- && methodCallExpression.Method.DeclaringType == typeof(EF)
- && methodCallExpression.Method.Name == nameof(EF.Constant))
+ && method.DeclaringType == typeof(EF)
+ && method.Name == nameof(EF.Constant))
{
// If this is a call to EF.Constant(), then examine its operand. If the operand isn't evaluatable (i.e. contains a reference
// to a database table), throw immediately.
@@ -197,6 +202,52 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
return Evaluate(operand, generateParameter: false);
}
+ // .NET 10 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans").
+ // Unfortunately, the LINQ interpreter does not support ref structs, so we rewrite e.g. MemoryExtensions.Contains to
+ // Enumerable.Contains here. See https://github.com/dotnet/runtime/issues/109757.
+ if (method.DeclaringType == typeof(MemoryExtensions) && !UseOldBehavior35100)
+ {
+ switch (method.Name)
+ {
+ case nameof(MemoryExtensions.Contains)
+ when methodCallExpression.Arguments is [var arg0, var arg1] &&
+ TryUnwrapSpanImplicitCast(arg0, out var unwrappedArg0):
+ {
+ return Visit(
+ Expression.Call(
+ EnumerableMethods.Contains.MakeGenericMethod(method.GetGenericArguments()[0]),
+ unwrappedArg0, arg1));
+ }
+
+ case nameof(MemoryExtensions.SequenceEqual)
+ when methodCallExpression.Arguments is [var arg0, var arg1]
+ && TryUnwrapSpanImplicitCast(arg0, out var unwrappedArg0)
+ && TryUnwrapSpanImplicitCast(arg1, out var unwrappedArg1):
+ return Visit(
+ Expression.Call(
+ EnumerableMethods.SequenceEqual.MakeGenericMethod(method.GetGenericArguments()[0]),
+ unwrappedArg0, unwrappedArg1));
+ }
+
+ static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
+ {
+ if (expression is MethodCallExpression
+ {
+ Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
+ Arguments: [var unwrapped]
+ }
+ && implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
+ && (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
+ {
+ result = unwrapped;
+ return true;
+ }
+
+ result = null;
+ return false;
+ }
+ }
+
return base.VisitMethodCall(methodCallExpression);
}
diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
index 02e75c9aa25..0c2135a68f4 100644
--- a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
+++ b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
@@ -23,6 +23,9 @@ namespace Microsoft.Data.Sqlite
/// Async Limitations
public partial class SqliteConnection : DbConnection
{
+ private static readonly bool UseOldBehavior35715 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35715", out var enabled35715) && enabled35715;
+
internal const string MainDatabaseName = "main";
private const int SQLITE_WIN32_DATA_DIRECTORY_TYPE = 1;
@@ -48,6 +51,8 @@ public partial class SqliteConnection : DbConnection
private static readonly StateChangeEventArgs _fromClosedToOpenEventArgs = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open);
private static readonly StateChangeEventArgs _fromOpenToClosedEventArgs = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed);
+ private static string[]? NativeDllSearchDirectories;
+
static SqliteConnection()
{
Type.GetType("SQLitePCL.Batteries_V2, SQLitePCLRaw.batteries_v2")
@@ -626,11 +631,82 @@ public virtual void LoadExtension(string file, string? proc = null)
private void LoadExtensionCore(string file, string? proc)
{
- var rc = sqlite3_load_extension(Handle, utf8z.FromString(file), utf8z.FromString(proc), out var errmsg);
- if (rc != SQLITE_OK)
+ if (UseOldBehavior35715)
+ {
+ var rc = sqlite3_load_extension(Handle, utf8z.FromString(file), utf8z.FromString(proc), out var errmsg);
+ if (rc != SQLITE_OK)
+ {
+ throw new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
+ }
+ }
+ else
+ {
+ SqliteException? firstException = null;
+ foreach (var path in GetLoadExtensionPaths(file))
+ {
+ var rc = sqlite3_load_extension(Handle, utf8z.FromString(path), utf8z.FromString(proc), out var errmsg);
+ if (rc == SQLITE_OK)
+ {
+ return;
+ }
+
+ if (firstException == null)
+ {
+ // We store the first exception so that error message looks more obvious if file appears in there
+ firstException = new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
+ }
+ }
+
+ if (firstException != null)
+ {
+ throw firstException;
+ }
+ }
+ }
+
+ private static IEnumerable GetLoadExtensionPaths(string file)
+ {
+ // we always try original input first
+ yield return file;
+
+ string? dirName = Path.GetDirectoryName(file);
+
+ // we don't try to guess directories for user, if they pass a path either absolute or relative - they're on their own
+ if (!string.IsNullOrEmpty(dirName))
{
- throw new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
+ yield break;
}
+
+ bool shouldTryAddingLibPrefix = !file.StartsWith("lib", StringComparison.Ordinal) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+
+ if (shouldTryAddingLibPrefix)
+ {
+ yield return $"lib{file}";
+ }
+
+ NativeDllSearchDirectories ??= GetNativeDllSearchDirectories();
+
+ foreach (string dir in NativeDllSearchDirectories)
+ {
+ yield return Path.Combine(dir, file);
+
+ if (shouldTryAddingLibPrefix)
+ {
+ yield return Path.Combine(dir, $"lib{file}");
+ }
+ }
+ }
+
+ private static string[] GetNativeDllSearchDirectories()
+ {
+ string? searchDirs = AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES") as string;
+
+ if (string.IsNullOrEmpty(searchDirs))
+ {
+ return Array.Empty();
+ }
+
+ return searchDirs!.Split(new[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
}
///