@@ -23,6 +23,9 @@ namespace Microsoft.Data.Sqlite
2323/// <seealso href="https://docs.microsoft.com/dotnet/standard/data/sqlite/async">Async Limitations</seealso>
2424public partial class SqliteConnection : DbConnection
2525{
26+ private static readonly bool UseOldBehavior35715 =
27+ AppContext . TryGetSwitch ( "Microsoft.EntityFrameworkCore.Issue35715" , out var enabled35715 ) && enabled35715 ;
28+
2629internal const string MainDatabaseName = "main" ;
2730
2831private const int SQLITE_WIN32_DATA_DIRECTORY_TYPE = 1 ;
@@ -48,6 +51,8 @@ public partial class SqliteConnection : DbConnection
4851private static readonly StateChangeEventArgs _fromClosedToOpenEventArgs = new StateChangeEventArgs ( ConnectionState . Closed , ConnectionState . Open ) ;
4952private static readonly StateChangeEventArgs _fromOpenToClosedEventArgs = new StateChangeEventArgs ( ConnectionState . Open , ConnectionState . Closed ) ;
5053
54+ private static string [ ] ? NativeDllSearchDirectories ;
55+
5156static SqliteConnection ( )
5257{
5358Type . GetType ( "SQLitePCL.Batteries_V2, SQLitePCLRaw.batteries_v2" )
@@ -626,11 +631,82 @@ public virtual void LoadExtension(string file, string? proc = null)
626631
627632private void LoadExtensionCore ( string file , string ? proc )
628633{
629- var rc = sqlite3_load_extension ( Handle , utf8z . FromString ( file ) , utf8z . FromString ( proc ) , out var errmsg ) ;
630- if ( rc != SQLITE_OK )
634+ if ( UseOldBehavior35715 )
635+ {
636+ var rc = sqlite3_load_extension ( Handle , utf8z . FromString ( file ) , utf8z . FromString ( proc ) , out var errmsg ) ;
637+ if ( rc != SQLITE_OK )
638+ {
639+ throw new SqliteException ( Resources . SqliteNativeError ( rc , errmsg . utf8_to_string ( ) ) , rc , rc ) ;
640+ }
641+ }
642+ else
643+ {
644+ SqliteException ? firstException = null ;
645+ foreach ( var path in GetLoadExtensionPaths ( file ) )
646+ {
647+ var rc = sqlite3_load_extension ( Handle , utf8z . FromString ( path ) , utf8z . FromString ( proc ) , out var errmsg ) ;
648+ if ( rc == SQLITE_OK )
649+ {
650+ return ;
651+ }
652+
653+ if ( firstException == null )
654+ {
655+ // We store the first exception so that error message looks more obvious if file appears in there
656+ firstException = new SqliteException ( Resources . SqliteNativeError ( rc , errmsg . utf8_to_string ( ) ) , rc , rc ) ;
657+ }
658+ }
659+
660+ if ( firstException != null )
661+ {
662+ throw firstException ;
663+ }
664+ }
665+ }
666+
667+ private static IEnumerable < string > GetLoadExtensionPaths ( string file )
668+ {
669+ // we always try original input first
670+ yield return file ;
671+
672+ string ? dirName = Path . GetDirectoryName ( file ) ;
673+
674+ // we don't try to guess directories for user, if they pass a path either absolute or relative - they're on their own
675+ if ( ! string . IsNullOrEmpty ( dirName ) )
631676{
632- throw new SqliteException ( Resources . SqliteNativeError ( rc , errmsg . utf8_to_string ( ) ) , rc , rc ) ;
677+ yield break ;
633678}
679+
680+ bool shouldTryAddingLibPrefix = ! file . StartsWith ( "lib" , StringComparison . Ordinal ) && ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ;
681+
682+ if ( shouldTryAddingLibPrefix )
683+ {
684+ yield return $ "lib{ file } ";
685+ }
686+
687+ NativeDllSearchDirectories ??= GetNativeDllSearchDirectories ( ) ;
688+
689+ foreach ( string dir in NativeDllSearchDirectories )
690+ {
691+ yield return Path . Combine ( dir , file ) ;
692+
693+ if ( shouldTryAddingLibPrefix )
694+ {
695+ yield return Path . Combine ( dir , $ "lib{ file } ") ;
696+ }
697+ }
698+ }
699+
700+ private static string [ ] GetNativeDllSearchDirectories ( )
701+ {
702+ string ? searchDirs = AppContext . GetData ( "NATIVE_DLL_SEARCH_DIRECTORIES" ) as string ;
703+
704+ if ( string . IsNullOrEmpty ( searchDirs ) )
705+ {
706+ return Array . Empty < string > ( ) ;
707+ }
708+
709+ return searchDirs ! . Split ( new [ ] { Path . PathSeparator } , StringSplitOptions . RemoveEmptyEntries ) ;
634710}
635711
636712/// <summary>